From 180297be8992248200b8b9e12ffed3998b31f1c3 Mon Sep 17 00:00:00 2001 From: harini-venkataraman Date: Tue, 19 May 2026 16:39:30 +0530 Subject: [PATCH 01/10] [MISC] Decommission prompt-service, old tools, and SDK1 prompt module (Phase 5) Remove prompt-service source, Dockerfiles, and docker-compose entries. Remove tools/classifier, tools/structure, tools/text_extractor directories. Remove SDK1 prompt.py module and its tests. Clean up PROMPT_HOST/PROMPT_PORT from backend settings, sample envs, docker configs, and CI workflows. Remove prompt-service from uv-lock scripts and production build workflow. Co-Authored-By: Claude Opus 4.6 --- .../workflows/docker-tools-build-push.yaml | 16 +- .github/workflows/production-build.yaml | 5 +- backend/backend/settings/base.py | 2 - backend/sample.env | 4 - docker/compose.debug.yaml | 16 - docker/docker-compose.build.yaml | 20 - docker/docker-compose.yaml | 20 - docker/dockerfiles/prompt.Dockerfile | 101 - .../prompt.Dockerfile.dockerignore | 67 - docker/sample.compose.override.yaml | 32 - docker/scripts/uv-lock-gen/README.md | 5 +- docker/scripts/uv-lock-gen/uv-lock.sh | 1 - prompt-service/.gitignore | 2 - prompt-service/.python-version | 1 - prompt-service/README.md | 1 - prompt-service/entrypoint.sh | 41 - prompt-service/pyproject.toml | 63 - prompt-service/sample.env | 79 - .../src/unstract/prompt_service/__init__.py | 0 .../src/unstract/prompt_service/config.py | 54 - .../src/unstract/prompt_service/constants.py | 203 -- .../prompt_service/controllers/__init__.py | 14 - .../controllers/answer_prompt.py | 709 ---- .../prompt_service/controllers/extraction.py | 53 - .../prompt_service/controllers/health.py | 8 - .../prompt_service/controllers/indexing.py | 102 - .../unstract/prompt_service/core/index_v2.py | 240 -- .../core/retrievers/automerging.py | 85 - .../core/retrievers/base_retriever.py | 61 - .../prompt_service/core/retrievers/fusion.py | 95 - .../core/retrievers/keyword_table.py | 80 - .../core/retrievers/recursive.py | 77 - .../core/retrievers/retriever_llm.py | 126 - .../prompt_service/core/retrievers/router.py | 157 - .../prompt_service/core/retrievers/simple.py | 51 - .../core/retrievers/subquestion.py | 65 - .../src/unstract/prompt_service/dto.py | 39 - .../src/unstract/prompt_service/exceptions.py | 56 - .../src/unstract/prompt_service/extensions.py | 36 - .../prompt_service/helpers/__init__.py | 0 .../unstract/prompt_service/helpers/auth.py | 85 - .../prompt_service/helpers/postprocessor.py | 114 - .../helpers/prompt_ide_base_tool.py | 57 - .../unstract/prompt_service/helpers/usage.py | 140 - .../helpers/variable_replacement.py | 204 -- .../src/unstract/prompt_service/run.py | 3 - .../prompt_service/services/__init__.py | 0 .../prompt_service/services/answer_prompt.py | 521 --- .../prompt_service/services/extraction.py | 92 - .../prompt_service/services/indexing.py | 94 - .../services/rentrolls_extractor/interface.py | 70 - .../prompt_service/services/retrieval.py | 156 - .../services/variable_replacement.py | 134 - .../unstract/prompt_service/tests/conftest.py | 29 - .../tests/integration/input/sample1.pdf | Bin 17597 -> 0 bytes .../tests/integration/test_api_endpoints.py | 85 - .../prompt_service/tests/sample.env.test | 24 - .../prompt_service/tests/unit/__init__.py | 0 .../prompt_service/tests/unit/conftest.py | 12 - .../tests/unit/test_retriever_llm.py | 315 -- .../unstract/prompt_service/utils/__init__.py | 0 .../unstract/prompt_service/utils/db_utils.py | 44 - .../prompt_service/utils/env_loader.py | 20 - .../prompt_service/utils/file_utils.py | 33 - .../utils/json_repair_helper.py | 64 - .../src/unstract/prompt_service/utils/log.py | 26 - .../unstract/prompt_service/utils/metrics.py | 8 - .../unstract/prompt_service/utils/request.py | 66 - prompt-service/uv.lock | 3025 ----------------- tools/classifier/.dockerignore | 3 - tools/classifier/Dockerfile | 41 - tools/classifier/README.md | 120 - tools/classifier/__init__.py | 0 tools/classifier/requirements.txt | 9 - tools/classifier/sample.env | 14 - tools/classifier/src/config/icon.svg | 53 - tools/classifier/src/config/properties.json | 71 - .../src/config/runtime_variables.json | 7 - tools/classifier/src/config/spec.json | 26 - tools/classifier/src/helper.py | 261 -- tools/classifier/src/main.py | 162 - tools/structure/.dockerignore | 3 - tools/structure/.gitignore | 1 - tools/structure/Dockerfile | 68 - tools/structure/README.md | 121 - tools/structure/__init__.py | 0 tools/structure/requirements.txt | 9 - tools/structure/sample.env | 16 - tools/structure/src/config/icon.svg | 53 - tools/structure/src/config/properties.json | 42 - .../src/config/runtime_variables.json | 5 - tools/structure/src/config/spec.json | 9 - tools/structure/src/constants.py | 108 - tools/structure/src/helpers.py | 134 - tools/structure/src/main.py | 772 ----- tools/structure/src/utils.py | 100 - tools/text_extractor/.dockerignore | 3 - tools/text_extractor/.gitignore | 162 - tools/text_extractor/Dockerfile | 42 - tools/text_extractor/README.md | 114 - tools/text_extractor/__init__.py | 0 tools/text_extractor/requirements.txt | 9 - tools/text_extractor/sample.env | 14 - tools/text_extractor/src/config/icon.svg | 53 - .../text_extractor/src/config/properties.json | 53 - .../src/config/runtime_variables.json | 18 - tools/text_extractor/src/config/spec.json | 7 - .../src/example_package/__init__.py | 0 tools/text_extractor/src/main.py | 109 - tools/text_extractor/tests/__init__.py | 0 unstract/sdk1/src/unstract/sdk1/prompt.py | 260 -- .../src/unstract/sdk1/utils/retry_utils.py | 7 - unstract/sdk1/tests/conftest.py | 4 - unstract/sdk1/tests/test_prompt.py | 174 - unstract/sdk1/tests/utils/test_retry_utils.py | 19 - .../unstract/workflow_execution/constants.py | 2 - .../workflow_execution/tools_utils.py | 4 - workers/sample.env | 5 - 118 files changed, 6 insertions(+), 11469 deletions(-) delete mode 100644 docker/dockerfiles/prompt.Dockerfile delete mode 100644 docker/dockerfiles/prompt.Dockerfile.dockerignore delete mode 100644 prompt-service/.gitignore delete mode 100644 prompt-service/.python-version delete mode 100644 prompt-service/README.md delete mode 100755 prompt-service/entrypoint.sh delete mode 100644 prompt-service/pyproject.toml delete mode 100644 prompt-service/sample.env delete mode 100644 prompt-service/src/unstract/prompt_service/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/config.py delete mode 100644 prompt-service/src/unstract/prompt_service/constants.py delete mode 100644 prompt-service/src/unstract/prompt_service/controllers/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py delete mode 100644 prompt-service/src/unstract/prompt_service/controllers/extraction.py delete mode 100644 prompt-service/src/unstract/prompt_service/controllers/health.py delete mode 100644 prompt-service/src/unstract/prompt_service/controllers/indexing.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/index_v2.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/automerging.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/base_retriever.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/fusion.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/keyword_table.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/recursive.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/retriever_llm.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/router.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/simple.py delete mode 100644 prompt-service/src/unstract/prompt_service/core/retrievers/subquestion.py delete mode 100644 prompt-service/src/unstract/prompt_service/dto.py delete mode 100644 prompt-service/src/unstract/prompt_service/exceptions.py delete mode 100644 prompt-service/src/unstract/prompt_service/extensions.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/auth.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/postprocessor.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/prompt_ide_base_tool.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/usage.py delete mode 100644 prompt-service/src/unstract/prompt_service/helpers/variable_replacement.py delete mode 100644 prompt-service/src/unstract/prompt_service/run.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/answer_prompt.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/extraction.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/indexing.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/rentrolls_extractor/interface.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/retrieval.py delete mode 100644 prompt-service/src/unstract/prompt_service/services/variable_replacement.py delete mode 100644 prompt-service/src/unstract/prompt_service/tests/conftest.py delete mode 100644 prompt-service/src/unstract/prompt_service/tests/integration/input/sample1.pdf delete mode 100644 prompt-service/src/unstract/prompt_service/tests/integration/test_api_endpoints.py delete mode 100644 prompt-service/src/unstract/prompt_service/tests/sample.env.test delete mode 100644 prompt-service/src/unstract/prompt_service/tests/unit/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/tests/unit/conftest.py delete mode 100644 prompt-service/src/unstract/prompt_service/tests/unit/test_retriever_llm.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/__init__.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/db_utils.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/env_loader.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/file_utils.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/json_repair_helper.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/log.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/metrics.py delete mode 100644 prompt-service/src/unstract/prompt_service/utils/request.py delete mode 100644 prompt-service/uv.lock delete mode 100644 tools/classifier/.dockerignore delete mode 100644 tools/classifier/Dockerfile delete mode 100644 tools/classifier/README.md delete mode 100644 tools/classifier/__init__.py delete mode 100644 tools/classifier/requirements.txt delete mode 100644 tools/classifier/sample.env delete mode 100644 tools/classifier/src/config/icon.svg delete mode 100644 tools/classifier/src/config/properties.json delete mode 100644 tools/classifier/src/config/runtime_variables.json delete mode 100644 tools/classifier/src/config/spec.json delete mode 100644 tools/classifier/src/helper.py delete mode 100644 tools/classifier/src/main.py delete mode 100644 tools/structure/.dockerignore delete mode 100644 tools/structure/.gitignore delete mode 100644 tools/structure/Dockerfile delete mode 100644 tools/structure/README.md delete mode 100644 tools/structure/__init__.py delete mode 100644 tools/structure/requirements.txt delete mode 100644 tools/structure/sample.env delete mode 100644 tools/structure/src/config/icon.svg delete mode 100644 tools/structure/src/config/properties.json delete mode 100644 tools/structure/src/config/runtime_variables.json delete mode 100644 tools/structure/src/config/spec.json delete mode 100644 tools/structure/src/constants.py delete mode 100644 tools/structure/src/helpers.py delete mode 100644 tools/structure/src/main.py delete mode 100644 tools/structure/src/utils.py delete mode 100644 tools/text_extractor/.dockerignore delete mode 100644 tools/text_extractor/.gitignore delete mode 100644 tools/text_extractor/Dockerfile delete mode 100644 tools/text_extractor/README.md delete mode 100644 tools/text_extractor/__init__.py delete mode 100644 tools/text_extractor/requirements.txt delete mode 100644 tools/text_extractor/sample.env delete mode 100644 tools/text_extractor/src/config/icon.svg delete mode 100644 tools/text_extractor/src/config/properties.json delete mode 100644 tools/text_extractor/src/config/runtime_variables.json delete mode 100644 tools/text_extractor/src/config/spec.json delete mode 100644 tools/text_extractor/src/example_package/__init__.py delete mode 100644 tools/text_extractor/src/main.py delete mode 100644 tools/text_extractor/tests/__init__.py delete mode 100644 unstract/sdk1/src/unstract/sdk1/prompt.py delete mode 100644 unstract/sdk1/tests/test_prompt.py diff --git a/.github/workflows/docker-tools-build-push.yaml b/.github/workflows/docker-tools-build-push.yaml index 439f4fbe81..01039ad345 100644 --- a/.github/workflows/docker-tools-build-push.yaml +++ b/.github/workflows/docker-tools-build-push.yaml @@ -10,12 +10,9 @@ on: service_name: description: "Tool to build" required: true - default: "tool-structure" # Provide a default value + default: "tool-sidecar" # Provide a default value type: choice options: # Define available options - - tool-classifier - - tool-structure - - tool-text-extractor - tool-sidecar add_latest_tag: description: "Also tag as 'latest'" @@ -58,16 +55,7 @@ jobs: - name: Set build configuration id: build-config run: | - if [ "${{ github.event.inputs.service_name }}" == "tool-classifier" ]; then - echo "context=." >> $GITHUB_OUTPUT - echo "dockerfile=./tools/classifier/Dockerfile" >> $GITHUB_OUTPUT - elif [ "${{ github.event.inputs.service_name }}" == "tool-structure" ]; then - echo "context=." >> $GITHUB_OUTPUT - echo "dockerfile=./tools/structure/Dockerfile" >> $GITHUB_OUTPUT - elif [ "${{ github.event.inputs.service_name }}" == "tool-text-extractor" ]; then - echo "context=." >> $GITHUB_OUTPUT - echo "dockerfile=./tools/text_extractor/Dockerfile" >> $GITHUB_OUTPUT - elif [ "${{ github.event.inputs.service_name }}" == "tool-sidecar" ]; then + if [ "${{ github.event.inputs.service_name }}" == "tool-sidecar" ]; then echo "context=." >> $GITHUB_OUTPUT echo "dockerfile=docker/dockerfiles/tool-sidecar.Dockerfile" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/production-build.yaml b/.github/workflows/production-build.yaml index 21edb520f7..8a9745b309 100644 --- a/.github/workflows/production-build.yaml +++ b/.github/workflows/production-build.yaml @@ -101,7 +101,6 @@ jobs: backend, frontend, platform-service, - prompt-service, runner, worker-unified, x2text-service, @@ -225,7 +224,7 @@ jobs: id: summary run: | # Initialize variables - TOTAL_SERVICES=7 + TOTAL_SERVICES=6 OVERALL_RESULT='${{ needs.build-and-push.result }}' SUCCESS_COUNT=0 FAILED_COUNT=0 @@ -316,7 +315,7 @@ jobs: echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY # Define services in order - for service in backend frontend platform-service prompt-service runner worker-unified x2text-service; do + for service in backend frontend platform-service runner worker-unified x2text-service; do if [ -f "build-status/${service}.json" ]; then STATUS=$(jq -r '.status' "build-status/${service}.json") if [ "$STATUS" = "success" ]; then diff --git a/backend/backend/settings/base.py b/backend/backend/settings/base.py index a77b44adaf..cb942d9c30 100644 --- a/backend/backend/settings/base.py +++ b/backend/backend/settings/base.py @@ -153,8 +153,6 @@ def get_required_setting(setting_key: str, default: str | None = None) -> str | FLIPT_BASE_URL = os.environ.get("FLIPT_BASE_URL", "http://localhost:9005") PLATFORM_HOST = os.environ.get("PLATFORM_SERVICE_HOST", "http://localhost") PLATFORM_PORT = os.environ.get("PLATFORM_SERVICE_PORT", 3001) -PROMPT_HOST = os.environ.get("PROMPT_HOST", "http://localhost") -PROMPT_PORT = os.environ.get("PROMPT_PORT", 3003) PROMPT_STUDIO_FILE_PATH = os.environ.get( "PROMPT_STUDIO_FILE_PATH", "/app/prompt-studio-data" ) diff --git a/backend/sample.env b/backend/sample.env index 2a69b753f8..58461aade3 100644 --- a/backend/sample.env +++ b/backend/sample.env @@ -90,10 +90,6 @@ UNSTRACT_RUNNER_API_TIMEOUT=240 # (in seconds) 2 mins UNSTRACT_RUNNER_API_RETRY_COUNT=5 # Number of retries for failed requests UNSTRACT_RUNNER_API_BACKOFF_FACTOR=3 # Exponential backoff factor for retries -# Prompt Service -PROMPT_HOST=http://unstract-prompt-service -PROMPT_PORT=3003 - #Prompt Studio PROMPT_STUDIO_FILE_PATH=/app/prompt-studio-data diff --git a/docker/compose.debug.yaml b/docker/compose.debug.yaml index 66d247997b..3522902b76 100644 --- a/docker/compose.debug.yaml +++ b/docker/compose.debug.yaml @@ -7,7 +7,6 @@ # 5678 - backend # 5679 - runner # 5680 - platform-service -# 5681 - prompt-service # 5682 - worker-file-processing-v2 # 5683 - worker-callback-v2 # 5684 - worker-api-deployment-v2 @@ -65,21 +64,6 @@ services: --graceful-timeout 5 unstract.platform_service.run:app" ] - prompt-service: - ports: - - "5681:5681" - command: [ - "uv run python -Xfrozen_modules=off -m debugpy --listen 0.0.0.0:5681 .venv/bin/gunicorn - --bind 0.0.0.0:3003 - --workers 1 - --threads 2 - --worker-class gthread - --log-level debug - --timeout 900 - --access-logfile - - --graceful-timeout 5 unstract.prompt_service.run:app" - ] - ######################################################################################################### # V2 Workers with debugpy # Using --pool=threads for debugpy compatibility (prefork doesn't work well with debugpy) diff --git a/docker/docker-compose.build.yaml b/docker/docker-compose.build.yaml index 3969b1f98f..db3b02c4e5 100644 --- a/docker/docker-compose.build.yaml +++ b/docker/docker-compose.build.yaml @@ -20,31 +20,11 @@ services: build: dockerfile: docker/dockerfiles/tool-sidecar.Dockerfile context: .. - tool-structure: - image: unstract/tool-structure:${VERSION} - build: - dockerfile: tools/structure/Dockerfile - context: .. - tool-text_extractor: - image: unstract/tool-text_extractor:${VERSION} - build: - dockerfile: tools/text_extractor/Dockerfile - context: .. - tool-classifier: - image: unstract/tool-classifier:${VERSION} - build: - dockerfile: tools/classifier/Dockerfile - context: .. platform-service: image: unstract/platform-service:${VERSION} build: dockerfile: docker/dockerfiles/platform.Dockerfile context: .. - prompt-service: - image: unstract/prompt-service:${VERSION} - build: - dockerfile: docker/dockerfiles/prompt.Dockerfile - context: .. x2text-service: image: unstract/x2text-service:${VERSION} build: diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index b7b49a7618..3406fa3d27 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -21,7 +21,6 @@ services: - minio - minio-bootstrap - platform-service - - prompt-service - x2text-service volumes: - prompt_studio_data:/app/prompt-studio-data @@ -151,25 +150,6 @@ services: labels: - traefik.enable=false - prompt-service: - image: unstract/prompt-service:${VERSION} - container_name: unstract-prompt-service - restart: unless-stopped - depends_on: - - db - - minio - - minio-bootstrap - - rabbitmq - ports: - - "3003:3003" - env_file: - - ../prompt-service/.env - labels: - - traefik.enable=false - extra_hosts: - # "host-gateway" is a special string that translates to host docker0 i/f IP. - - "host.docker.internal:host-gateway" - x2text-service: image: unstract/x2text-service:${VERSION} container_name: unstract-x2text-service diff --git a/docker/dockerfiles/prompt.Dockerfile b/docker/dockerfiles/prompt.Dockerfile deleted file mode 100644 index cca9161d81..0000000000 --- a/docker/dockerfiles/prompt.Dockerfile +++ /dev/null @@ -1,101 +0,0 @@ -# Use a specific version of Python slim image -FROM python:3.12-slim-trixie AS base - -LABEL maintainer="Zipstack Inc." \ - description="Prompt Service Container" - -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - PYTHONPATH=/unstract \ - BUILD_CONTEXT_PATH=prompt-service \ - BUILD_PACKAGES_PATH=unstract \ - TARGET_PLUGINS_PATH=src/unstract/prompt_service/plugins \ - APP_USER=unstract \ - APP_HOME=/app \ - # OpenTelemetry configuration (disabled by default, enable in docker-compose) - OTEL_TRACES_EXPORTER=none \ - OTEL_METRICS_EXPORTER=none \ - OTEL_LOGS_EXPORTER=none \ - OTEL_SERVICE_NAME=unstract_prompt - -# Install system dependencies, create user, and setup directories in one layer -RUN apt-get update \ - && apt-get --no-install-recommends install -y \ - build-essential \ - libmagic-dev \ - pkg-config \ - git \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* \ - && adduser -u 5678 --disabled-password --gecos "" ${APP_USER} \ - && mkdir -p ${APP_HOME} \ - && chown -R ${APP_USER}:${APP_USER} ${APP_HOME} - -# Install uv package manager -COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /uvx /bin/ - -# Create working directory -WORKDIR ${APP_HOME} - -# ----------------------------------------------- -# EXTERNAL DEPENDENCIES STAGE - This layer gets cached if external dependencies don't change -# ----------------------------------------------- -FROM base AS ext-dependencies - -# Copy dependency-related files -COPY --chown=${APP_USER}:${APP_USER} ${BUILD_CONTEXT_PATH}/pyproject.toml ${BUILD_CONTEXT_PATH}/uv.lock ${BUILD_CONTEXT_PATH}/README.md ./ - -# Copy local package dependencies -COPY --chown=${APP_USER}:${APP_USER} ${BUILD_PACKAGES_PATH}/sdk1 /unstract/sdk1 -COPY --chown=${APP_USER}:${APP_USER} ${BUILD_PACKAGES_PATH}/core /unstract/core -COPY --chown=${APP_USER}:${APP_USER} ${BUILD_PACKAGES_PATH}/flags /unstract/flags - -# Increase timeout for large packages (flipt-client is ~45MB) -ENV UV_HTTP_TIMEOUT=120 - -# Switch to non-root user -USER ${APP_USER} - -# Install external dependencies from pyproject.toml -RUN uv sync --group deploy --locked --no-install-project --no-dev - -# ----------------------------------------------- -# FINAL STAGE - Minimal image for production -# ----------------------------------------------- -FROM ext-dependencies AS production - -# Set shell options for better error handling -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# Copy application code (this layer changes most frequently) -COPY --chown=${APP_USER}:${APP_USER} ${BUILD_CONTEXT_PATH} ./ - -# Switch to non-root user -USER ${APP_USER} - -# Install just the application in editable mode -RUN uv sync --group deploy --locked - -# Install plugins after copying source code -RUN for dir in "${TARGET_PLUGINS_PATH}"/*/; do \ - dirpath=${dir%*/}; \ - dirname=${dirpath##*/}; \ - if [ "${dirname}" != "*" ]; then \ - echo "Installing plugin: ${dirname}..." && \ - uv pip install "${TARGET_PLUGINS_PATH}/${dirname}"; \ - fi; \ - done && \ - uv run opentelemetry-bootstrap -a requirements | uv pip install --requirement - && \ - # Use OpenTelemetry v1 - v2 breaks LiteLLM with instrumentation enabled - uv pip uninstall opentelemetry-instrumentation-openai-v2 && \ - uv pip install opentelemetry-instrumentation-openai - -# Create required directories -RUN mkdir -p prompt-studio-data - -EXPOSE 3003 - -ARG VERSION=dev -ENV UNSTRACT_APPS_VERSION=${VERSION} - -CMD ["./entrypoint.sh"] diff --git a/docker/dockerfiles/prompt.Dockerfile.dockerignore b/docker/dockerfiles/prompt.Dockerfile.dockerignore deleted file mode 100644 index f2cffbf5be..0000000000 --- a/docker/dockerfiles/prompt.Dockerfile.dockerignore +++ /dev/null @@ -1,67 +0,0 @@ -**/__pycache__ -**/.mypy_cache -**/.pytest_cache -**/.python-version -**/.pyc -**/.pyo -**/.venv -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.gitkeep -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/bin -**/charts -**/docker-compose* -**/compose* -**/Dockerfile* -**/build -**/dist -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -**/.db -**/.sqlite3 -**/.log -**/*-log.txt -**/*.drawio -**/.tmp -**/.swp -**/.swo -**/.bak -*.idea -*.vscode -*.git -!LICENSE -*.md -!README.md -# But include the agent prompt markdown files -!*_system_prompt.md -.jshintrc -.pre-commit-config.yaml -**/tests -test*.py -**/*.egg-info - -backend -frontend -platform-service -!prompt-service -tools -unstract -!unstract/core -!unstract/flags -!unstract/sdk1 -runner -x2text-service diff --git a/docker/sample.compose.override.yaml b/docker/sample.compose.override.yaml index e762d45e79..a5a52cdd49 100644 --- a/docker/sample.compose.override.yaml +++ b/docker/sample.compose.override.yaml @@ -138,38 +138,6 @@ services: - action: rebuild path: ../platform-service/uv.lock - ######################################################################################################### - # Prompt Service (memory optimized: 1 worker, 2 threads) - prompt-service: - image: unstract/prompt-service:${VERSION} - build: - dockerfile: docker/dockerfiles/prompt.Dockerfile - context: .. - entrypoint: ["bash", "-c"] - command: [ - "uv run python -Xfrozen_modules=off .venv/bin/gunicorn - --bind 0.0.0.0:3003 - --workers 1 - --threads 2 - --worker-class gthread - --log-level debug - --timeout 900 - --access-logfile - - --graceful-timeout 5 unstract.prompt_service.run:app" - ] - develop: - watch: - - action: sync+restart - path: ../prompt-service/ - target: /app - ignore: [.venv/, __pycache__/, "*.pyc", .pytest_cache/, .mypy_cache/, node_modules/] - - action: sync+restart - path: ../unstract/ - target: /unstract - ignore: [.venv/, __pycache__/, "*.pyc", .pytest_cache/, .mypy_cache/] - - action: rebuild - path: ../prompt-service/uv.lock - ######################################################################################################### # X2Text Service x2text-service: diff --git a/docker/scripts/uv-lock-gen/README.md b/docker/scripts/uv-lock-gen/README.md index f5c565640e..2c37fa758f 100644 --- a/docker/scripts/uv-lock-gen/README.md +++ b/docker/scripts/uv-lock-gen/README.md @@ -2,11 +2,10 @@ Helps generate uv's lockfiles by running `uv sync` on all necessary packages and services. -It also detects **transitive dependency changes** — if a local path dependency's `pyproject.toml` changed (e.g. `unstract/sdk1`), all services that depend on it (e.g. `backend`, `prompt-service`) will have their lockfiles regenerated too. +It also detects **transitive dependency changes** — if a local path dependency's `pyproject.toml` changed (e.g. `unstract/sdk1`), all services that depend on it (e.g. `backend`, `workers`) will have their lockfiles regenerated too. - project root - `backend` -- `prompt-service` - `runner` - `unstract/core` - `unstract/flags` @@ -22,5 +21,5 @@ Can be run without any arguments to check for lockfile generation on all necessa Accepts a list of directories to generate for as command line arguments ```shell -./uv-lock.sh backend prompt-service +./uv-lock.sh backend platform-service ``` diff --git a/docker/scripts/uv-lock-gen/uv-lock.sh b/docker/scripts/uv-lock-gen/uv-lock.sh index 9c61bf0ed5..7180b9fd56 100755 --- a/docker/scripts/uv-lock-gen/uv-lock.sh +++ b/docker/scripts/uv-lock-gen/uv-lock.sh @@ -100,7 +100,6 @@ list_descendants () directories=( "." "backend" - "prompt-service" "platform-service" "runner" "x2text-service" diff --git a/prompt-service/.gitignore b/prompt-service/.gitignore deleted file mode 100644 index ae2f699ad0..0000000000 --- a/prompt-service/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Plugins -src/unstract/prompt_service/plugins diff --git a/prompt-service/.python-version b/prompt-service/.python-version deleted file mode 100644 index f3fe474aee..0000000000 --- a/prompt-service/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12.9 diff --git a/prompt-service/README.md b/prompt-service/README.md deleted file mode 100644 index a6f313e012..0000000000 --- a/prompt-service/README.md +++ /dev/null @@ -1 +0,0 @@ -#TODO : Add README.md diff --git a/prompt-service/entrypoint.sh b/prompt-service/entrypoint.sh deleted file mode 100755 index a2eef06dc0..0000000000 --- a/prompt-service/entrypoint.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -show_help() { - echo "Usage: ./entrypoint.sh [OPTIONS]" - echo "" - echo "Options:" - echo " --dev Run Gunicorn in development mode with --reload and reduced graceful timeout (5s)." - echo " --help, -h Show this help message and exit." -} - -# Parse arguments -dev=false - -while [[ "$#" -gt 0 ]]; do - case $1 in - --dev) dev=true ;; - --help|-h) show_help; exit 0 ;; - *) echo "Unknown argument: $1"; exit 1 ;; - esac - shift -done - -gunicorn_args=( - --bind 0.0.0.0:3003 - --workers 2 - --threads 2 - --worker-class gthread - --log-level debug - --timeout 900 - --access-logfile - -) - -if [ "$dev" = true ]; then - echo "Running in development mode" - gunicorn_args+=(--reload --graceful-timeout 5) -else - echo "Running in production mode" -fi - -# Start Gunicorn -.venv/bin/gunicorn "${gunicorn_args[@]}" unstract.prompt_service.run:app diff --git a/prompt-service/pyproject.toml b/prompt-service/pyproject.toml deleted file mode 100644 index ae32c95201..0000000000 --- a/prompt-service/pyproject.toml +++ /dev/null @@ -1,63 +0,0 @@ -[project] -name = "unstract-prompt-service" -version = "0.0.1" -description = "Unstract's prompt studio helper" -authors = [{ name = "Zipstack Inc.", email = "devsupport@zipstack.com" }] -requires-python = ">=3.12,<3.13" -readme = "README.md" -# license = {text = "MIT"} - -dependencies = [ - "peewee~=3.16", - "nltk~=3.8", - "flask~=3.0", - "llama-index>=0.14.13", - "python-dotenv==1.0.1", - "json-repair~=0.42.0", - "requests>=2.28,<3.0", - "redis>=5.0.3,<5.3", - "unstract-core", - "unstract-flags", - "unstract-sdk1[aws,gcs,azure]" -] - -[tool.uv.sources] -unstract-flags = { path = "../unstract/flags", editable = true } -unstract-core = { path = "../unstract/core", editable = true } -unstract-sdk1 = { path = "../unstract/sdk1", editable = true } - -[dependency-groups] -test = [ - "pytest~=8.0.1", - "pytest-asyncio>=0.23.0", - "pytest-dotenv==0.5.2", - "pytest-mock~=3.14.0", - "pytest-md-report>=0.6.2", - "python-dotenv==1.0.1", - "flask-WTF~=1.1", -] -deploy = [ - "gunicorn~=23.0", - # OpenTelemetry for tracing and profiling - "opentelemetry-distro", - "opentelemetry-exporter-otlp", -] -dev = ["poethepoet>=0.33.1", "debugpy>=1.8.14"] - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src/unstract"] - -[tool.poe] -envfile = ".env" - -[tool.poe.tasks.prompt-service] -cmd = "./entrypoint.sh" -help = "Runs the Unstract prompt service (Gunicorn)" - -[tool.poe.tasks.prompt-service-flask] -cmd = "uv run flask --app src/unstract/prompt_service/run.py run --port 3003" -help = "Runs the Unstract prompt service (Flask)" diff --git a/prompt-service/sample.env b/prompt-service/sample.env deleted file mode 100644 index 01d021f9d1..0000000000 --- a/prompt-service/sample.env +++ /dev/null @@ -1,79 +0,0 @@ -# Backend DB -PG_BE_HOST=unstract-db -PG_BE_PORT=5432 -PG_BE_USERNAME=unstract_dev -PG_BE_PASSWORD=unstract_pass -PG_BE_DATABASE=unstract_db -DB_SCHEMA="unstract" - -# Redis -REDIS_HOST="unstract-redis" -REDIS_PORT=6379 -REDIS_PASSWORD="" -REDIS_USER=default - -# Redis Sentinel mode - set to True for HA deployments (K8s only) -# When True: point REDIS_HOST to Sentinel K8s service DNS, REDIS_PORT to 26379 -# REDIS_PASSWORD is reused for Sentinel auth. -REDIS_SENTINEL_MODE=False -REDIS_SENTINEL_MASTER_NAME=mymaster - -# Logging -LOG_LEVEL=INFO - - -### Env from `unstract-core` ### -# Celery for PublishLogs -CELERY_BROKER_BASE_URL="amqp://unstract-rabbitmq:5672//" -CELERY_BROKER_USER=admin -CELERY_BROKER_PASS=password -# Logs Expiry of 24 hours -LOGS_EXPIRATION_TIME_IN_SECOND=86400 - - -### Env from `unstract-flags` ### -# Feature Flags -EVALUATION_SERVER_IP=unstract-flipt -EVALUATION_SERVER_PORT=9000 -PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python -# Flipt Service -FLIPT_SERVICE_AVAILABLE=False - - -### Env from `unstract-sdk` ### -# Platform Service -PLATFORM_SERVICE_HOST=http://unstract-platform-service -PLATFORM_SERVICE_PORT=3001 - -# X2Text Service -X2TEXT_HOST=http://unstract-x2text-service -X2TEXT_PORT=3004 - -# Remote storage related envs -PERMANENT_REMOTE_STORAGE='{"provider": "minio", "credentials": {"endpoint_url": "http://unstract-minio:9000", "key": "minio", "secret": "minio123"}}' -TEMPORARY_REMOTE_STORAGE='{"provider": "minio", "credentials": {"endpoint_url": "http://unstract-minio:9000", "key": "minio", "secret": "minio123"}}' -REMOTE_PROMPT_STUDIO_FILE_PATH="unstract/prompt-studio-data/" - -# Timeout for LLMW (v2) extraction -ADAPTER_LLMW_WAIT_TIMEOUT=900 # 15 mins - -# Control async extraction of LLMWhisperer (v1) -# Time in seconds to wait before polling LLMWhisperer's status API -ADAPTER_LLMW_POLL_INTERVAL=30 -# Total number of times to poll the status API. -# 500 mins to allow 1500 (max pages limit) * 20 (approx time in sec to process a page) -ADAPTER_LLMW_MAX_POLLS=1000 -# Number of times to retry the /whisper-status API before failing the extraction -ADAPTER_LLMW_STATUS_RETRIES=5 -# Retry backoff for LLMWhisperer client (v2) -# Max retry attempts for transient HTTP errors (429, 5xx). Set 0 to disable. -ADAPTER_LLMW_MAX_RETRIES=3 -# Min backoff wait in seconds between retries -ADAPTER_LLMW_RETRY_MIN_WAIT=1.0 -# Max backoff wait in seconds between retries -ADAPTER_LLMW_RETRY_MAX_WAIT=60.0 - -### Env for Rentroll Service ### -# Rentroll Service -RENTROLL_SERVICE_HOST=http://unstract-rentroll-service -RENTROLL_SERVICE_PORT=5003 diff --git a/prompt-service/src/unstract/prompt_service/__init__.py b/prompt-service/src/unstract/prompt_service/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prompt-service/src/unstract/prompt_service/config.py b/prompt-service/src/unstract/prompt_service/config.py deleted file mode 100644 index 5edfb829ed..0000000000 --- a/prompt-service/src/unstract/prompt_service/config.py +++ /dev/null @@ -1,54 +0,0 @@ -import logging -import warnings -from os import environ as env -from pathlib import Path - -from dotenv import load_dotenv -from flask import Flask - -from unstract.core.flask import ( - PluginManager, - register_error_handlers, - register_request_id_middleware, -) -from unstract.core.flask.logging import setup_logging -from unstract.prompt_service.controllers import api -from unstract.sdk1.constants import LogLevel - -load_dotenv() - - -def create_app() -> Flask: - """Creates and configures the Flask application.""" - log_level = env.get("LOG_LEVEL", LogLevel.INFO.value).upper() - setup_logging(log_level) - - # Suppress OpenTelemetry EventLogger LogRecord deprecation warning - # This is a bug in OpenTelemetry SDK 1.37.0 where EventLogger.emit() still uses - # deprecated trace_id/span_id/trace_flags parameters instead of context parameter. - # See: https://github.com/open-telemetry/opentelemetry-python/issues/4328 - # TODO: Remove this suppression once OpenTelemetry fixes EventLogger.emit() - warnings.filterwarnings( - "ignore", - message="LogRecord init with.*trace_id.*span_id.*trace_flags.*deprecated", - category=DeprecationWarning, - module="opentelemetry.sdk._events", - ) - - log_level = getattr(logging, log_level, logging.INFO) - app = Flask("prompt-service") - app.logger.setLevel(log_level) - app.logger.info("Initializing Flask application...") - - # Load plugins - plugins_dir = Path(__file__).parent / "plugins" - plugins_pkg = "unstract.prompt_service.plugins" - manager = PluginManager(app, plugins_dir, plugins_pkg) - manager.load_plugins() - - register_request_id_middleware(app) - register_error_handlers(app) - app.register_blueprint(api) - - app.logger.info("Flask app created successfully.") - return app diff --git a/prompt-service/src/unstract/prompt_service/constants.py b/prompt-service/src/unstract/prompt_service/constants.py deleted file mode 100644 index 9eddab8423..0000000000 --- a/prompt-service/src/unstract/prompt_service/constants.py +++ /dev/null @@ -1,203 +0,0 @@ -from enum import Enum - - -class PromptServiceConstants: - """Constants used in the prompt service.""" - - WORD = "word" - SYNONYMS = "synonyms" - OUTPUTS = "outputs" - TOOL_ID = "tool_id" - RUN_ID = "run_id" - EXECUTION_ID = "execution_id" - FILE_NAME = "file_name" - FILE_HASH = "file_hash" - NAME = "name" - ACTIVE = "active" - PROMPT = "prompt" - CHUNK_SIZE = "chunk-size" - PROMPTX = "promptx" - VECTOR_DB = "vector-db" - EMBEDDING = "embedding" - X2TEXT_ADAPTER = "x2text_adapter" - CHUNK_OVERLAP = "chunk-overlap" - LLM = "llm" - IS_ASSERT = "is_assert" - ASSERTION_FAILURE_PROMPT = "assertion_failure_prompt" - RETRIEVAL_STRATEGY = "retrieval-strategy" - TYPE = "type" - NUMBER = "number" - EMAIL = "email" - DATE = "date" - BOOLEAN = "boolean" - JSON = "json" - PREAMBLE = "preamble" - SIMILARITY_TOP_K = "similarity-top-k" - PROMPT_TOKENS = "prompt_tokens" - COMPLETION_TOKENS = "completion_tokens" - TOTAL_TOKENS = "total_tokens" - RESPONSE = "response" - POSTAMBLE = "postamble" - GRAMMAR = "grammar" - PLATFORM_SERVICE_API_KEY = "PLATFORM_SERVICE_API_KEY" - EMBEDDING_SUFFIX = "embedding_suffix" - EVAL_SETTINGS = "eval_settings" - EVAL_SETTINGS_EVALUATE = "evaluate" - EVAL_SETTINGS_MONITOR_LLM = "monitor_llm" - EVAL_SETTINGS_EXCLUDE_FAILED = "exclude_failed" - TOOL_SETTINGS = "tool_settings" - LOG_EVENTS_ID = "log_events_id" - CHALLENGE_LLM = "challenge_llm" - CHALLENGE = "challenge" - ENABLE_CHALLENGE = "enable_challenge" - EXTRACTION = "extraction" - SUMMARIZE = "summarize" - SINGLE_PASS_EXTRACTION = "single-pass-extraction" - SIMPLE_PROMPT_STUDIO = "simple-prompt-studio" - LLM_USAGE_REASON = "llm_usage_reason" - METADATA = "metadata" - OUTPUT = "output" - CONTEXT = "context" - INCLUDE_METADATA = "include_metadata" - TABLE = "table" - TABLE_SETTINGS = "table_settings" - EPILOGUE = "epilogue" - PLATFORM_POSTAMBLE = "platform_postamble" - WORD_CONFIDENCE_POSTAMBLE = "word_confidence_postamble" - HIGHLIGHT_DATA_PLUGIN = "highlight-data" - SUMMARIZE_AS_SOURCE = "summarize_as_source" - VARIABLE_MAP = "variable_map" - RECORD = "record" - CUSTOM_DATA = "custom_data" - TEXT = "text" - ENABLE_HIGHLIGHT = "enable_highlight" - ENABLE_WORD_CONFIDENCE = "enable_word_confidence" - FILE_PATH = "file_path" - HIGHLIGHT_DATA = "highlight_data" - CONFIDENCE_DATA = "confidence_data" - WORD_CONFIDENCE_DATA = "word_confidence_data" - REQUIRED_FIELDS = "required_fields" - REQUIRED = "required" - EXECUTION_SOURCE = "execution_source" - METRICS = "metrics" - CAPTURE_METRICS = "capture_metrics" - LINE_ITEM = "line-item" - LINE_NUMBERS = "line_numbers" - WHISPER_HASH = "whisper_hash" - PAID_FEATURE_MSG = ( - "It is a cloud / enterprise feature. If you have purchased a plan and still " - "face this issue, please contact support" - ) - NO_CONTEXT_ERROR = ( - "Couldn't fetch context from vector DB. " - "This happens usually due to a delay by the Vector DB " - "provider to confirm writes to DB. " - "Please try again after some time" - ) - COMBINED_PROMPT = "combined_prompt" - TOOL = "tool" - JSON_POSTAMBLE = "JSON_POSTAMBLE" - DEFAULT_JSON_POSTAMBLE = "Wrap the final JSON result inbetween §§§ like below example:\n§§§\n\n§§§" - DOCUMENT_TYPE = "document_type" - # Webhook postprocessing settings - ENABLE_POSTPROCESSING_WEBHOOK = "enable_postprocessing_webhook" - POSTPROCESSING_WEBHOOK_URL = "postprocessing_webhook_url" - - -class RunLevel(Enum): - """Different stages of prompt execution. - - Comprises of prompt run and response evaluation stages. - """ - - RUN = "RUN" - EVAL = "EVAL" - CHALLENGE = "CHALLENGE" - TABLE_EXTRACTION = "TABLE_EXTRACTION" - - -class DBTableV2: - """Database tables.""" - - ORGANIZATION = "organization" - ADAPTER_INSTANCE = "adapter_instance" - PROMPT_STUDIO_REGISTRY = "prompt_studio_registry" - PLATFORM_KEY = "platform_key" - TOKEN_USAGE = "usage" - - -class FileStorageKeys: - """File storage keys.""" - - PERMANENT_REMOTE_STORAGE = "PERMANENT_REMOTE_STORAGE" - TEMPORARY_REMOTE_STORAGE = "TEMPORARY_REMOTE_STORAGE" - - -class FileStorageType(Enum): - """File storage type.""" - - PERMANENT = "permanent" - TEMPORARY = "temporary" - - -class ExecutionSource(Enum): - """Execution source.""" - - IDE = "ide" - TOOL = "tool" - - -class VariableType(str, Enum): - """Type of variable.""" - - STATIC = "STATIC" - DYNAMIC = "DYNAMIC" - CUSTOM_DATA = "CUSTOM_DATA" - - -class RetrievalStrategy(str, Enum): - """Available retrieval strategies for prompt service.""" - - SIMPLE = "simple" - SUBQUESTION = "subquestion" - FUSION = "fusion" - RECURSIVE = "recursive" - ROUTER = "router" - KEYWORD_TABLE = "keyword_table" - AUTOMERGING = "automerging" - - -class VariableConstants: - """Constants for variable extraction.""" - - VARIABLE_REGEX = "{{(.+?)}}" - DYNAMIC_VARIABLE_DATA_REGEX = r"\[(.*?)\]" - DYNAMIC_VARIABLE_URL_REGEX = ( - r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»" - "'']))" - ) # noqa: E501 - CUSTOM_DATA_VARIABLE_REGEX = r"custom_data\.([a-zA-Z0-9_\.]+)" - - -class IndexingConstants: - TOOL_ID = "tool_id" - EMBEDDING_INSTANCE_ID = "embedding_instance_id" - VECTOR_DB_INSTANCE_ID = "vector_db_instance_id" - X2TEXT_INSTANCE_ID = "x2text_instance_id" - FILE_PATH = "file_path" - CHUNK_SIZE = "chunk_size" - CHUNK_OVERLAP = "chunk_overlap" - REINDEX = "reindex" - FILE_HASH = "file_hash" - OUTPUT_FILE_PATH = "output_file_path" - ENABLE_HIGHLIGHT = "enable_highlight" - ENABLE_WORD_CONFIDENCE = "enable_word_confidence" - USAGE_KWARGS = "usage_kwargs" - PROCESS_TEXT = "process_text" - EXTRACTED_TEXT = "extracted_text" - TAGS = "tags" - EXECUTION_SOURCE = "execution_source" - DOC_ID = "doc_id" - TOOL_EXECUTION_METATADA = "tool_execution_metadata" - EXECUTION_DATA_DIR = "execution_data_dir" - METADATA_FILE = "METADATA.json" diff --git a/prompt-service/src/unstract/prompt_service/controllers/__init__.py b/prompt-service/src/unstract/prompt_service/controllers/__init__.py deleted file mode 100644 index e6b149de21..0000000000 --- a/prompt-service/src/unstract/prompt_service/controllers/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from flask import Blueprint - -from .answer_prompt import answer_prompt_bp -from .extraction import extraction_bp -from .health import health_bp -from .indexing import indexing_bp - -api = Blueprint("api", __name__) - -# Register blueprint to the API Blueprint -api.register_blueprint(health_bp) -api.register_blueprint(answer_prompt_bp) -api.register_blueprint(indexing_bp) -api.register_blueprint(extraction_bp) diff --git a/prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py b/prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py deleted file mode 100644 index fdf0deaae1..0000000000 --- a/prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py +++ /dev/null @@ -1,709 +0,0 @@ -"""Published API Controller""" - -from typing import Any - -from flask import Blueprint, request -from flask import current_app as app - -from unstract.core.flask import PluginManager -from unstract.core.flask.exceptions import APIError -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.constants import RetrievalStrategy, RunLevel -from unstract.prompt_service.exceptions import BadRequest -from unstract.prompt_service.helpers.auth import AuthHelper -from unstract.prompt_service.helpers.prompt_ide_base_tool import PromptServiceBaseTool -from unstract.prompt_service.helpers.usage import UsageHelper -from unstract.prompt_service.services.answer_prompt import AnswerPromptService -from unstract.prompt_service.services.rentrolls_extractor.interface import ( - RentRollExtractor, -) -from unstract.prompt_service.services.retrieval import RetrievalService -from unstract.prompt_service.services.variable_replacement import ( - VariableReplacementService, -) -from unstract.prompt_service.utils.file_utils import FileUtils -from unstract.prompt_service.utils.log import publish_log -from unstract.sdk1.constants import LogLevel -from unstract.sdk1.embedding import EmbeddingCompat -from unstract.sdk1.exceptions import SdkError -from unstract.sdk1.index import Index -from unstract.sdk1.llm import LLM -from unstract.sdk1.platform import PlatformHelper as ToolAdapter -from unstract.sdk1.vector_db import VectorDB - -answer_prompt_bp = Blueprint("answer-prompt", __name__) - - -@AuthHelper.auth_required -@answer_prompt_bp.route("/answer-prompt", methods=["POST"]) -def prompt_processor() -> Any: - platform_key = AuthHelper.get_token_from_auth_header(request) - payload: dict[Any, Any] = request.json - if not payload: - raise BadRequest - tool_settings = payload.get(PSKeys.TOOL_SETTINGS, {}) - enable_challenge = tool_settings.get(PSKeys.ENABLE_CHALLENGE, False) - # Rename "outputs" to "prompts" in payload - prompts = payload.get(PSKeys.OUTPUTS, []) - tool_id: str = payload.get(PSKeys.TOOL_ID, "") - run_id: str = payload.get(PSKeys.RUN_ID, "") - execution_id: str = payload.get(PSKeys.EXECUTION_ID, "") - file_hash = payload.get(PSKeys.FILE_HASH) - file_path = payload.get(PSKeys.FILE_PATH) - doc_name = str(payload.get(PSKeys.FILE_NAME, "")) - log_events_id: str = payload.get(PSKeys.LOG_EVENTS_ID, "") - custom_data: dict[str, Any] = payload.get(PSKeys.CUSTOM_DATA, {}) - structured_output: dict[str, Any] = {} - metadata: dict[str, Any] = { - PSKeys.RUN_ID: run_id, - PSKeys.FILE_NAME: doc_name, - PSKeys.CONTEXT: {}, - PSKeys.REQUIRED_FIELDS: {}, - } - metrics: dict = {} - variable_names: list[str] = [] - # Identifier for source of invocation - execution_source = payload.get(PSKeys.EXECUTION_SOURCE, "") - context_retrieval_metrics = {} - publish_log( - log_events_id, - {"tool_id": tool_id, "run_id": run_id, "doc_name": doc_name}, - LogLevel.DEBUG, - RunLevel.RUN, - f"Preparing to execute '{len(prompts)}' prompt(s)", - ) - # Rename "output" to "prompt" - for output in prompts: # type:ignore - variable_names.append(output[PSKeys.NAME]) - metadata[PSKeys.REQUIRED_FIELDS][output[PSKeys.NAME]] = output.get( - PSKeys.REQUIRED, None - ) - - for output in prompts: # type:ignore - prompt_name = output[PSKeys.NAME] - prompt_text = output[PSKeys.PROMPT] - chunk_size = output[PSKeys.CHUNK_SIZE] - app.logger.info(f"[{tool_id}] chunk size: {chunk_size}") - util = PromptServiceBaseTool(platform_key=platform_key) - index = Index(tool=util, run_id=run_id, capture_metrics=True) - if VariableReplacementService.is_variables_present(prompt_text=prompt_text): - # Determine if this is from IDE (Prompt Studio) or API deployment - is_ide = execution_source == "ide" - prompt_text = VariableReplacementService.replace_variables_in_prompt( - prompt=output, - structured_output=structured_output, - log_events_id=log_events_id, - tool_id=tool_id, - prompt_name=prompt_name, - doc_name=doc_name, - custom_data=custom_data, - is_ide=is_ide, - ) - - app.logger.info(f"[{tool_id}] Executing prompt: '{prompt_name}'") - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.RUN, - "Executing prompt", - ) - - # Finding and replacing the variables in the prompt - # The variables are in the form %variable_name% - - output[PSKeys.PROMPTX] = AnswerPromptService.extract_variable( - structured_output, variable_names, output, prompt_text - ) - - doc_id = index.generate_index_key( - file_hash=file_hash, - vector_db=output[PSKeys.VECTOR_DB], - embedding=output[PSKeys.EMBEDDING], - x2text=output[PSKeys.X2TEXT_ADAPTER], - chunk_size=str(output[PSKeys.CHUNK_SIZE]), - chunk_overlap=str(output[PSKeys.CHUNK_OVERLAP]), - ) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.RUN, - "Retrieved document ID", - ) - - try: - usage_kwargs = {"run_id": run_id, "execution_id": execution_id} - llm = LLM( - adapter_instance_id=output[PSKeys.LLM], - tool=util, - usage_kwargs={ - **usage_kwargs, - PSKeys.LLM_USAGE_REASON: PSKeys.EXTRACTION, - }, - capture_metrics=True, - ) - - # Only create embedding and vector_db if chunk_size > 0 - # When chunk_size is 0, we read the complete file without embeddings - embedding = None - vector_db = None - if chunk_size > 0: - embedding = EmbeddingCompat( - adapter_instance_id=output[PSKeys.EMBEDDING], - tool=util, - kwargs={ - **usage_kwargs, - }, - ) - - vector_db = VectorDB( - tool=util, - adapter_instance_id=output[PSKeys.VECTOR_DB], - embedding=embedding, - ) - except SdkError as e: - msg = f"Couldn't fetch adapter. {e}" - app.logger.error(msg) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.ERROR, - RunLevel.RUN, - "Unable to obtain LLM / embedding / vectorDB", - ) - # Preserve the status code from the SDK error - status_code = getattr(e, "status_code", None) or 500 - raise APIError(message=msg, code=status_code) from e - - if output[PSKeys.TYPE] == PSKeys.TABLE: - adapter_parent_data = ToolAdapter.get_adapter_config(util, output[PSKeys.LLM]) - llm_config = adapter_parent_data.get("adapter_metadata") - adapter_id = adapter_parent_data.get("adapter_id") - adapter_prefix = adapter_id.split("|")[0] - llm_provider = llm.kwargs.get("provider") - llm_adapter_config = {"adapter_id": adapter_prefix} - llm_adapter_config["provider"] = llm_provider - if adapter_prefix == "azureopenai": - llm_adapter_config["model"] = llm_config.get("model") - llm_adapter_config["api_key"] = llm_config.get("api_key") - llm_adapter_config["api_base"] = llm_config.get("azure_endpoint") - llm_adapter_config["max_retries"] = llm_config.get("max_retries") - llm_adapter_config["timeout"] = llm_config.get("timeout") - llm_adapter_config["deployment"] = llm_config.get("deployment_name") - llm_adapter_config["api_version"] = llm_config.get("api_version") - if adapter_prefix == "openai": - llm_adapter_config["model"] = llm_config.get("model") - llm_adapter_config["api_key"] = llm_config.get("api_key") - llm_adapter_config["api_base"] = llm_config.get("api_base") - llm_adapter_config["max_retries"] = llm_config.get("max_retries") - llm_adapter_config["timeout"] = llm_config.get("timeout") - if adapter_prefix == "anthropic": - llm_adapter_config["model"] = llm_config.get("model") - llm_adapter_config["api_key"] = llm_config.get("api_key") - llm_adapter_config["max_retries"] = llm_config.get("max_retries") - llm_adapter_config["timeout"] = llm_config.get("timeout") - if adapter_prefix == "bedrock": - llm_adapter_config["model"] = llm_config.get("model") - llm_adapter_config["aws_access_key_id"] = llm_config.get( - "aws_access_key_id" - ) - llm_adapter_config["aws_secret_access_key"] = llm_config.get( - "aws_secret_access_key" - ) - llm_adapter_config["max_retries"] = llm_config.get("max_retries") - llm_adapter_config["budget_tokens"] = llm_config.get("budget_tokens") - llm_adapter_config["max_tokens"] = llm_config.get("max_tokens") - llm_adapter_config["region_name"] = llm_config.get("region_name") - llm_adapter_config["timeout"] = llm_config.get("timeout") - table_settings = output[PSKeys.TABLE_SETTINGS] - document_type: str = table_settings.get(PSKeys.DOCUMENT_TYPE) - app.logger.info("Document type: %s", document_type) - if document_type == "rent_rolls": - # Create RentRollExtractor instance and process the extraction - extractor = RentRollExtractor() - fs_instance = FileUtils.get_fs_instance(execution_source=execution_source) - extracted_data = fs_instance.read(path=file_path, mode="r") - app.logger.info("Starting rent roll extraction run...") - try: - extraction_result = extractor.process( - extractor_settings=output, - extracted_data=extracted_data, - llm_config=llm_adapter_config, - schema=prompt_text, - ) - - # Update structured output with the extraction result - structured_output[output[PSKeys.NAME]] = extraction_result["data"] - response = { - PSKeys.OUTPUT: structured_output, - PSKeys.METADATA: extraction_result["metrics"], - PSKeys.METRICS: extraction_result["metrics"], - } - - # Track token usage by sending to the audit service - try: - from unstract.sdk1.utils.token_counter import TokenCounter - - # Get metrics from the extraction result - metrics = extraction_result.get("metrics", {}) - - # Create token counter adapter from metrics - token_usage = metrics.get("token_usage") or {} - token_counter = TokenCounter( - input_tokens=token_usage.get("prompt_tokens"), - output_tokens=token_usage.get("completion_tokens"), - ) - - # Extract model name from llm config - # Extract model name from llm config - model_info = metrics.get("model_info") or {} - model_name = model_info.get("model_name") - provider = model_info.get("provider") - - # Prepare usage data for audit - kwargs = { - "workflow_id": "", # Not applicable for rent rolls - "execution_id": "", # Not applicable for rent rolls - "adapter_instance_id": adapter_id, - "run_id": str(run_id), - "provider": provider, - "llm_usage_reason": "extraction", - } - - # Push usage data to audit service - UsageHelper.push_usage_data( - event_type="extraction", - kwargs=kwargs, - platform_api_key=platform_key, - token_counter=token_counter, - model_name=model_name, - ) - app.logger.info( - "Successfully pushed token usage data to audit service" - ) - except Exception as e: - app.logger.error(f"Failed to track token usage: {str(e)}") - app.logger.info("Rent roll extraction completed successfully") - return response - except Exception as e: - app.logger.error(f"Failed to process rent roll: {str(e)}") - raise e - if document_type != "rent_rolls": - try: - structured_output = AnswerPromptService.extract_table( - output=output, - structured_output=structured_output, - llm=llm, - execution_source=execution_source, - prompt=prompt_text, - ) - metadata = UsageHelper.query_usage_metadata( - token=platform_key, metadata=metadata - ) - response = { - PSKeys.METADATA: metadata, - PSKeys.OUTPUT: structured_output, - PSKeys.METRICS: metrics, - } - return response - except APIError as api_error: - app.logger.error( - "Failed to extract table for the prompt %s: %s", - output[PSKeys.NAME], - str(api_error), - ) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.ERROR, - RunLevel.TABLE_EXTRACTION, - "Error while extracting table for the prompt", - ) - raise api_error - elif output[PSKeys.TYPE] == PSKeys.LINE_ITEM: - try: - structured_output = AnswerPromptService.extract_line_item( - tool_settings=tool_settings, - output=output, - structured_output=structured_output, - llm=llm, - file_path=file_path, - metadata=metadata, - execution_source=execution_source, - ) - continue - except APIError as e: - app.logger.error( - "Failed to extract line-item for the prompt %s: %s", - output[PSKeys.NAME], - str(e), - ) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.ERROR, - RunLevel.RUN, - "Error while extracting line-item for the prompt", - ) - raise e - - try: - answer = "NA" - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.INFO, - RunLevel.RUN, - "Retrieving context from adapter", - ) - - retrieval_strategy = output.get(PSKeys.RETRIEVAL_STRATEGY) - - # Get all valid retrieval strategies - valid_strategies = {strategy.value for strategy in RetrievalStrategy} - - if retrieval_strategy in valid_strategies: - app.logger.info(f"[{tool_id}] Performing retrieval for : {file_path}") - answer, context = RetrievalService.perform_retrieval( - tool_settings=tool_settings, - output=output, - doc_id=doc_id, - llm=llm, - vector_db=vector_db, # This will be None when chunk_size is 0 - retrieval_type=retrieval_strategy, - metadata=metadata, - chunk_size=chunk_size, - execution_source=execution_source, - file_path=file_path, - context_retrieval_metrics=context_retrieval_metrics, - ) - metadata[PSKeys.CONTEXT][output[PSKeys.NAME]] = context - else: - app.logger.info( - "Invalid retrieval strategy passed: %s", - retrieval_strategy, - ) - - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.RUN, - "Retrieved context from adapter", - ) - - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.INFO, - RunLevel.RUN, - f"Processing prompt type: {output[PSKeys.TYPE]}", - ) - - if output[PSKeys.TYPE] == PSKeys.NUMBER: - if answer.lower() == "na": - structured_output[output[PSKeys.NAME]] = None - else: - # Extract these prompts as constants after pkging - prompt = f"Extract the number from the following \ - text:\n{answer}\n\nOutput just the number. \ - If the number is expressed in millions \ - or thousands, expand the number to its numeric value \ - The number should be directly assignable\ - to a numeric variable.\ - It should not have any commas, \ - percentages or other grouping \ - characters. No explanation is required.\ - If you cannot extract the number, output 0." - answer = AnswerPromptService.run_completion( - llm=llm, - prompt=prompt, - ) - try: - structured_output[output[PSKeys.NAME]] = float(answer) - except Exception as e: - app.logger.info( - f"Error parsing response (to numeric, float): {e}", - LogLevel.ERROR, - ) - structured_output[output[PSKeys.NAME]] = None - elif output[PSKeys.TYPE] == PSKeys.EMAIL: - if answer.lower() == "na": - structured_output[output[PSKeys.NAME]] = None - else: - prompt = f'Extract the email from the following text:\n{answer}\n\nOutput just the email. \ - The email should be directly assignable to a string variable. \ - No explanation is required. If you cannot extract the email, output "NA".' # noqa - answer = AnswerPromptService.run_completion( - llm=llm, - prompt=prompt, - ) - structured_output[output[PSKeys.NAME]] = answer - elif output[PSKeys.TYPE] == PSKeys.DATE: - if answer.lower() == "na": - structured_output[output[PSKeys.NAME]] = None - else: - prompt = f'Extract the date from the following text:\n{answer}\n\nOutput just the date.\ - The date should be in ISO date time format. No explanation is required. \ - The date should be directly assignable to a date variable. \ - If you cannot convert the string into a date, output "NA".' # noqa - answer = AnswerPromptService.run_completion( - llm=llm, - prompt=prompt, - ) - structured_output[output[PSKeys.NAME]] = answer - - elif output[PSKeys.TYPE] == PSKeys.BOOLEAN: - if answer.lower() == "na": - structured_output[output[PSKeys.NAME]] = None - else: - prompt = f'Extract yes/no from the following text:\n{answer}\n\n\ - Output in single word.\ - If the context is trying to convey that the answer is true, \ - then return "yes", else return "no".' - answer = AnswerPromptService.run_completion( - llm=llm, - prompt=prompt, - ) - if answer.lower() == "yes": - structured_output[output[PSKeys.NAME]] = True - else: - structured_output[output[PSKeys.NAME]] = False - elif output[PSKeys.TYPE] == PSKeys.JSON: - AnswerPromptService.handle_json( - answer=answer, - structured_output=structured_output, - output=output, - log_events_id=log_events_id, - tool_id=tool_id, - doc_name=doc_name, - llm=llm, - enable_highlight=tool_settings.get(PSKeys.ENABLE_HIGHLIGHT, False), - enable_word_confidence=tool_settings.get( - PSKeys.ENABLE_WORD_CONFIDENCE, False - ), - execution_source=execution_source, - metadata=metadata, - file_path=file_path, - ) - else: - structured_output[output[PSKeys.NAME]] = answer - - # If there is a trailing '\n' remove it - if isinstance(structured_output[output[PSKeys.NAME]], str): - structured_output[output[PSKeys.NAME]] = structured_output[ - output[PSKeys.NAME] - ].rstrip("\n") - - # Challenge condition - if enable_challenge: - challenge_plugin: dict[str, Any] = PluginManager().get_plugin( - PSKeys.CHALLENGE - ) - try: - if challenge_plugin: - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.INFO, - RunLevel.CHALLENGE, - "Challenging response", - ) - challenge_llm = LLM( - adapter_instance_id=tool_settings[PSKeys.CHALLENGE_LLM], - tool=util, - usage_kwargs={ - **usage_kwargs, - PSKeys.LLM_USAGE_REASON: PSKeys.CHALLENGE, - }, - capture_metrics=True, - ) - challenge = challenge_plugin["entrypoint_cls"]( - llm=llm, - challenge_llm=challenge_llm, - run_id=run_id, - context="\n".join(context), - tool_settings=tool_settings, - output=output, - structured_output=structured_output, - platform_key=platform_key, - metadata=metadata, - ) - # Will inline replace the structured output passed. - challenge.run() - else: - app.logger.info( - "No challenge plugin found to evaluate prompt: %s", - output[PSKeys.NAME], - ) - except challenge_plugin["exception_cls"] as e: - app.logger.error( - "Failed to challenge prompt %s: %s", - output[PSKeys.NAME], - str(e), - ) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.ERROR, - RunLevel.CHALLENGE, - "Error while challenging response", - ) - - # - # Evaluate the prompt. - # - if ( - PSKeys.EVAL_SETTINGS in output - and output[PSKeys.EVAL_SETTINGS][PSKeys.EVAL_SETTINGS_EVALUATE] - ): - eval_plugin: dict[str, Any] = PluginManager().get_plugin("evaluation") - if eval_plugin: - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.INFO, - RunLevel.EVAL, - "Evaluating response", - ) - try: - evaluator = eval_plugin["entrypoint_cls"]( - "", - "".join(context), - "", - "", - output, - structured_output, - platform_key, - ) - # Will inline replace the structured output passed. - evaluator.run() - except eval_plugin["exception_cls"] as e: - app.logger.error( - f"Failed to evaluate prompt {output[PSKeys.NAME]}: {str(e)}" - ) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.ERROR, - RunLevel.EVAL, - "Error while evaluation", - ) - else: - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.EVAL, - "Evaluation completed", - ) - else: - app.logger.info( - f"No eval plugin found to evaluate prompt: {output[PSKeys.NAME]}" # noqa: E501 - ) - finally: - challenge_metrics = ( - {f"{challenge_llm.get_usage_reason()}_llm": challenge_llm.get_metrics()} - if enable_challenge and challenge_llm - else {} - ) - metrics.setdefault(prompt_name, {}).update( - { - "context_retrieval": context_retrieval_metrics.get(prompt_name, {}), - f"{llm.get_usage_reason()}_llm": llm.get_metrics(), - **challenge_metrics, - } - ) - # Only close vector_db if it was created (chunk_size > 0) - if vector_db: - vector_db.close() - publish_log( - log_events_id, - {"tool_id": tool_id, "doc_name": doc_name}, - LogLevel.INFO, - RunLevel.RUN, - "Sanitizing null values", - ) - for k, v in structured_output.items(): - if isinstance(v, str) and v.lower() == "na": - structured_output[k] = None - elif isinstance(v, list): - for i in range(len(v)): - if isinstance(v[i], str) and v[i].lower() == "na": - v[i] = None - elif isinstance(v[i], dict): - for k1, v1 in v[i].items(): - if isinstance(v1, str) and v1.lower() == "na": - v[i][k1] = None - elif isinstance(v, dict): - for k1, v1 in v.items(): - if isinstance(v1, str) and v1.lower() == "na": - v[k1] = None - - publish_log( - log_events_id, - {"tool_id": tool_id, "doc_name": doc_name}, - LogLevel.INFO, - RunLevel.RUN, - "Execution complete", - ) - metadata = UsageHelper.query_usage_metadata(token=platform_key, metadata=metadata) - response = { - PSKeys.METADATA: metadata, - PSKeys.OUTPUT: structured_output, - PSKeys.METRICS: metrics, - } - return response diff --git a/prompt-service/src/unstract/prompt_service/controllers/extraction.py b/prompt-service/src/unstract/prompt_service/controllers/extraction.py deleted file mode 100644 index 516894f429..0000000000 --- a/prompt-service/src/unstract/prompt_service/controllers/extraction.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Any - -from flask import Blueprint, request - -from unstract.prompt_service.constants import IndexingConstants as IKeys -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.helpers.auth import AuthHelper -from unstract.prompt_service.services.extraction import ExtractionService -from unstract.prompt_service.utils.request import validate_request_payload - -extraction_bp = Blueprint("extract", __name__) - -REQUIRED_FIELDS = [ - "x2text_instance_id", - "file_path", - "execution_source", - "run_id", -] - - -@AuthHelper.auth_required -@extraction_bp.route("/extract", methods=["POST"]) -def extract() -> Any: - platform_key = AuthHelper.get_token_from_auth_header(request) - payload: dict[Any, Any] = request.json - validate_request_payload(payload, REQUIRED_FIELDS) - - x2text_instance_id: str = payload.get(IKeys.X2TEXT_INSTANCE_ID, "") - file_path: str = payload.get(IKeys.FILE_PATH, "") - output_file_path: str | None = payload.get(IKeys.OUTPUT_FILE_PATH, "") - enable_highlight: bool = payload.get(IKeys.ENABLE_HIGHLIGHT, False) - usage_kwargs: dict[Any, Any] = payload.get(IKeys.USAGE_KWARGS, {}) - run_id: str = payload.get(PSKeys.RUN_ID, "") - execution_source = payload.get(IKeys.EXECUTION_SOURCE, None) - tags: str = payload.get(IKeys.TAGS, "") - tool_exec_metadata = payload.get(IKeys.TOOL_EXECUTION_METATADA, {}) - execution_run_data_folder = payload.get(IKeys.EXECUTION_DATA_DIR, "") - - extracted_text = ExtractionService.perform_extraction( - file_path=file_path, - x2text_instance_id=x2text_instance_id, - output_file_path=output_file_path, - enable_highlight=enable_highlight, - usage_kwargs=usage_kwargs, - run_id=run_id, - execution_source=execution_source, - platform_key=platform_key, - tags=tags, - tool_exec_metadata=tool_exec_metadata, - execution_run_data_folder=execution_run_data_folder, - ) - response = {IKeys.EXTRACTED_TEXT: extracted_text} - return response diff --git a/prompt-service/src/unstract/prompt_service/controllers/health.py b/prompt-service/src/unstract/prompt_service/controllers/health.py deleted file mode 100644 index 59cc428210..0000000000 --- a/prompt-service/src/unstract/prompt_service/controllers/health.py +++ /dev/null @@ -1,8 +0,0 @@ -from flask import Blueprint - -health_bp = Blueprint("health", __name__) - - -@health_bp.route("/health", methods=["GET"]) -def health_check() -> str: - return "OK" diff --git a/prompt-service/src/unstract/prompt_service/controllers/indexing.py b/prompt-service/src/unstract/prompt_service/controllers/indexing.py deleted file mode 100644 index f9416a9c92..0000000000 --- a/prompt-service/src/unstract/prompt_service/controllers/indexing.py +++ /dev/null @@ -1,102 +0,0 @@ -import logging -from typing import Any - -from flask import Blueprint, request - -from unstract.prompt_service.constants import IndexingConstants as IKeys -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.dto import ( - ChunkingConfig, - FileInfo, - InstanceIdentifiers, - ProcessingOptions, -) -from unstract.prompt_service.helpers.auth import AuthHelper -from unstract.prompt_service.services.indexing import IndexingService -from unstract.prompt_service.utils.request import validate_request_payload - -indexing_bp = Blueprint("index", __name__) -logger = logging.getLogger(__name__) - -REQUIRED_FIELDS = [ - "tool_id", - "extracted_text", - "embedding_instance_id", - "vector_db_instance_id", - "x2text_instance_id", - "file_path", - "chunk_size", - "chunk_overlap", - "execution_source", - "run_id", -] - - -@AuthHelper.auth_required -@indexing_bp.route("/index", methods=["POST"]) -def index() -> Any: - """Endpoint for indexing documents into the vector database. - - This API accepts a JSON payload containing document details, processes the - document, and stores it in the vector database for retrieval. - - Raises: - BadRequest: If the request payload is missing or invalid. - - Returns: - str: doc_id - """ - platform_key = AuthHelper.get_token_from_auth_header(request) - payload: dict[Any, Any] = request.json - validate_request_payload(payload, REQUIRED_FIELDS) - - tool_id: str = payload.get(IKeys.TOOL_ID, "") - embedding_instance_id: str = payload.get(IKeys.EMBEDDING_INSTANCE_ID, "") - vector_db_instance_id: str = payload.get(IKeys.VECTOR_DB_INSTANCE_ID, "") - x2text_instance_id: str = payload.get(IKeys.X2TEXT_INSTANCE_ID, "") - file_path: str = payload.get(IKeys.FILE_PATH, "") - file_hash: str | None = payload.get(IKeys.FILE_HASH) - chunk_size: int = payload.get(IKeys.CHUNK_SIZE, 512) # Default chunk size - chunk_overlap: int = payload.get(IKeys.CHUNK_OVERLAP, 128) # Default chunk overlap - reindex: bool = payload.get(IKeys.REINDEX, False) - enable_highlight: bool = payload.get(IKeys.ENABLE_HIGHLIGHT, False) - enable_word_confidence: bool = payload.get(IKeys.ENABLE_WORD_CONFIDENCE, False) - usage_kwargs: dict[Any, Any] = payload.get(IKeys.USAGE_KWARGS, {}) - extracted_text: str = payload.get(IKeys.EXTRACTED_TEXT, "") - tags: list[str] = payload.get(IKeys.TAGS, None) - execution_source = payload.get(IKeys.EXECUTION_SOURCE, None) - run_id: str = payload.get(PSKeys.RUN_ID, "") - - instance_identifiers = InstanceIdentifiers( - embedding_instance_id=embedding_instance_id, - vector_db_instance_id=vector_db_instance_id, - x2text_instance_id=x2text_instance_id, - tool_id=tool_id, - tags=tags, - llm_instance_id=None, - ) - - file_info = FileInfo(file_path=file_path, file_hash=file_hash) - - chunking_config = ChunkingConfig(chunk_size=chunk_size, chunk_overlap=chunk_overlap) - - processing_options = ProcessingOptions( - reindex=reindex, - enable_highlight=enable_highlight, - enable_word_confidence=enable_word_confidence, - usage_kwargs=usage_kwargs, - ) - doc_id = IndexingService.index( - chunking_config=chunking_config, - execution_source=execution_source, - run_id=run_id, - file_info=file_info, - instance_identifiers=instance_identifiers, - platform_key=platform_key, - processing_options=processing_options, - extracted_text=extracted_text, - ) - response = { - IKeys.DOC_ID: doc_id, - } - return response diff --git a/prompt-service/src/unstract/prompt_service/core/index_v2.py b/prompt-service/src/unstract/prompt_service/core/index_v2.py deleted file mode 100644 index 5f1703b320..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/index_v2.py +++ /dev/null @@ -1,240 +0,0 @@ -import json -import logging -from typing import Any - -import openai -from llama_index.core import Document -from llama_index.core.vector_stores import ( - FilterOperator, - MetadataFilter, - MetadataFilters, - VectorStoreQuery, - VectorStoreQueryResult, -) - -from unstract.prompt_service.dto import ( - ChunkingConfig, - FileInfo, - InstanceIdentifiers, - ProcessingOptions, -) -from unstract.sdk1.adapters.vectordb.no_op.src.no_op_custom_vectordb import ( - NoOpCustomVectorDB, -) -from unstract.sdk1.constants import LogLevel -from unstract.sdk1.embedding import Embedding -from unstract.sdk1.exceptions import SdkError, parse_litellm_err -from unstract.sdk1.file_storage.impl import FileStorage -from unstract.sdk1.file_storage.provider import FileStorageProvider -from unstract.sdk1.platform import PlatformHelper as ToolAdapter -from unstract.sdk1.tool.stream import StreamMixin -from unstract.sdk1.utils.common import Utils, capture_metrics -from unstract.sdk1.utils.tool import ToolUtils -from unstract.sdk1.vector_db import VectorDB - -logger = logging.getLogger(__name__) - - -class Index: - def __init__( - self, - tool: StreamMixin, - instance_identifiers: InstanceIdentifiers, - chunking_config: ChunkingConfig, - processing_options: ProcessingOptions, - run_id: str | None = None, - capture_metrics: bool = False, - ): - self.tool = tool - self._run_id = run_id - self._capture_metrics = capture_metrics - self.instance_identifiers = instance_identifiers - self.chunking_config = chunking_config - self.processing_options = processing_options - self._metrics = {} - - def generate_index_key( - self, - file_info: FileInfo, - fs: FileStorage = FileStorage(provider=FileStorageProvider.LOCAL), - ) -> str: - """Generates a unique index key based on the provided configuration, - file information, instance identifiers, and processing options. - - Args: - chunking_config : ChunkingConfig - file_info (FileInfo): Contains file-related - information such as path and hash. - instance_identifiers (InstanceIdentifiers): Identifiers for - embedding, vector DB, tool, etc. - processing_options (ProcessingOptions): Options related to reindexing, - highlighting, and processing text. - fs (FileStorage, optional): File storage for remote storage. - - Returns: - str: A unique index key used for indexing the document. - """ - if not file_info.file_path and not file_info.file_hash: - raise ValueError("One of `file_path` or `file_hash` need to be provided") - - if not file_info.file_hash: - file_hash = fs.get_hash_from_file(path=file_info.file_path) - - # Whole adapter config is used currently even though it contains some keys - # which might not be relevant to indexing. This is easier for now than - # marking certain keys of the adapter config as necessary. - vector_db_config = ToolAdapter.get_adapter_config( - self.tool, self.instance_identifiers.vector_db_instance_id - ) - embedding_config = ToolAdapter.get_adapter_config( - self.tool, self.instance_identifiers.embedding_instance_id - ) - x2text_config = ToolAdapter.get_adapter_config( - self.tool, self.instance_identifiers.x2text_instance_id - ) - Utils.strip_adapter_name(vector_db_config, embedding_config, x2text_config) - index_key = { - "file_hash": file_hash, - "vector_db_config": vector_db_config, - "embedding_config": embedding_config, - "x2text_config": x2text_config, - # Typed and hashed as strings since the final hash is persisted - # and this is required to be backward compatible - "chunk_size": str(self.chunking_config.chunk_size), - "chunk_overlap": str(self.chunking_config.chunk_overlap), - } - # JSON keys are sorted to ensure that the same key gets hashed even in - # case where the fields are reordered. - hashed_index_key = ToolUtils.hash_str(json.dumps(index_key, sort_keys=True)) - return hashed_index_key - - @capture_metrics - def is_document_indexed( - self, - doc_id: str, - embedding: Embedding, - vector_db: VectorDB, - ) -> bool: - """Checks if nodes are already present in the vector database for a - given doc_id. - - Returns: - str: The document ID. - """ - # Checking if document is already indexed against doc_id - doc_id_eq_filter = MetadataFilter.from_dict( - {"key": "doc_id", "operator": FilterOperator.EQ, "value": doc_id} - ) - filters = MetadataFilters(filters=[doc_id_eq_filter]) - q = VectorStoreQuery( - query_embedding=embedding.get_query_embedding(" "), - doc_ids=[doc_id], - filters=filters, - ) - - doc_id_found = False - try: - n: VectorStoreQueryResult = vector_db.query(query=q) - if len(n.nodes) > 0: - doc_id_found = True - self.tool.stream_log(f"Found {len(n.nodes)} nodes for {doc_id}") - else: - self.tool.stream_log(f"No nodes found for {doc_id}") - except Exception as e: - logger.warning( - f"Error querying {self.instance_identifiers.vector_db_instance_id}:" - f" {str(e)}, proceeding to index", - exc_info=True, - ) - - if doc_id_found and not self.processing_options.reindex: - self.tool.stream_log(f"File was indexed already under {doc_id}") - return doc_id_found - - return doc_id_found - - @capture_metrics - def perform_indexing( - self, - vector_db: VectorDB, - doc_id: str, - extracted_text: str, - doc_id_found: bool, - ): - if isinstance( - vector_db.get_vector_db( - adapter_instance_id=self.instance_identifiers.vector_db_instance_id, - embedding_dimension=1, - ), - (NoOpCustomVectorDB), - ): - return doc_id - - self.tool.stream_log("Indexing file...") - full_text = [ - { - "section": "full", - "text_contents": str(extracted_text), - } - ] - # Convert raw text to llama index usage Document - documents = self._prepare_documents(doc_id, full_text) - if self.processing_options.reindex and doc_id_found: - self.delete_nodes(vector_db, doc_id) - self._trigger_indexing(vector_db, documents) - return doc_id - - def _trigger_indexing(self, vector_db, documents): - self.tool.stream_log("Adding nodes to vector db...") - try: - vector_db.index_document( - documents, - chunk_size=self.chunking_config.chunk_size, - chunk_overlap=self.chunking_config.chunk_overlap, - show_progress=True, - ) - self.tool.stream_log("File has been indexed successfully") - # Handle embedding errors from litellm - except openai.OpenAIError as e: - e = parse_litellm_err(e) - raise e - except Exception as e: - self.tool.stream_log( - f"Error adding nodes to vector db: {e}", - level=LogLevel.ERROR, - ) - raise e - - def delete_nodes(self, vector_db: VectorDB, doc_id: str): - try: - vector_db.delete(ref_doc_id=doc_id) - self.tool.stream_log(f"Deleted nodes for {doc_id}") - except Exception as e: - self.tool.stream_log( - f"Error deleting nodes for {doc_id}: {e}", - level=LogLevel.ERROR, - ) - raise SdkError(f"Error deleting nodes for {doc_id}: {e}") from e - - def _prepare_documents(self, doc_id: str, full_text: Any) -> list: - documents = [] - try: - for item in full_text: - text = item["text_contents"] - document = Document( - text=text, - doc_id=doc_id, - metadata={"section": item["section"]}, - ) - document.id_ = doc_id - documents.append(document) - self.tool.stream_log(f"Number of documents: {len(documents)}") - return documents - except Exception as e: - self.tool.stream_log( - f"Error while processing documents {doc_id}: {e}", - level=LogLevel.ERROR, - ) - raise SdkError( - f"Error while processing documents for indexing {doc_id}: {e}" - ) from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/automerging.py b/prompt-service/src/unstract/prompt_service/core/retrievers/automerging.py deleted file mode 100644 index 3df2a3776e..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/automerging.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging - -from llama_index.core import VectorStoreIndex -from llama_index.core.retrievers import AutoMergingRetriever as LlamaAutoMergingRetriever -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class AutomergingRetriever(BaseRetriever): - """Automerging retrieval using LlamaIndex's native AutoMergingRetriever. - - This retriever merges smaller chunks into larger ones when the smaller chunks - don't contain enough information, providing better context for answers. - """ - - def retrieve(self) -> set[str]: - """Retrieve text chunks using LlamaIndex's native AutoMergingRetriever. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - logger.info( - f"Retrieving chunks for {self.doc_id} using LlamaIndex AutoMergingRetriever." - ) - - # Get the vector store index - vector_store_index: VectorStoreIndex = self.vector_db.get_vector_store_index() - - # Create base vector retriever with metadata filters - base_retriever = vector_store_index.as_retriever( - similarity_top_k=self.top_k, - filters=MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ), - ) - - # Try to use native AutoMergingRetriever - try: - # Create AutoMergingRetriever with the base retriever - auto_merging_retriever = LlamaAutoMergingRetriever( - base_retriever, - storage_context=self.vector_db.get_storage_context() - if hasattr(self.vector_db, "get_storage_context") - else None, - verbose=False, - ) - - # Retrieve nodes using auto-merging - nodes = auto_merging_retriever.retrieve(self.prompt) - - except Exception as e: - logger.error(f"AutoMergingRetriever failed : {e}") - raise RetrievalError(f"AutoMergingRetriever failed: {str(e)}") from e - - # Extract unique text chunks - chunks: set[str] = set() - for node in nodes: - if node.score > 0: - chunks.add(node.get_content()) - else: - logger.info( - f"Node score is less than 0. " - f"Ignored: {node.node_id} with score {node.score}" - ) - - logger.info( - f"Successfully retrieved {len(chunks)} chunks using AutoMergingRetriever." - ) - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during auto-merging retrieval for {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error( - f"Unexpected error during auto-merging retrieval for {self.doc_id}: {e}" - ) - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/base_retriever.py b/prompt-service/src/unstract/prompt_service/core/retrievers/base_retriever.py deleted file mode 100644 index 288223c00a..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/base_retriever.py +++ /dev/null @@ -1,61 +0,0 @@ -from unstract.prompt_service.core.retrievers.retriever_llm import RetrieverLLM -from unstract.sdk1.llm import LLM -from unstract.sdk1.vector_db import VectorDB - - -class BaseRetriever: - def __init__( - self, - vector_db: VectorDB, - prompt: str, - doc_id: str, - top_k: int, - llm: LLM | None = None, - ): - """Initialize the Retrieval class. - - Args: - vector_db (VectorDB): The vector database instance. - prompt (str): The query prompt. - doc_id (str): Document identifier for query context. - top_k (int): Number of top results to retrieve. - """ - self.vector_db = vector_db - self.prompt = prompt - self.doc_id = doc_id - self.top_k = top_k - self._llm: LLM | None = llm - self._retriever_llm: RetrieverLLM | None = None - - @property - def llm(self) -> RetrieverLLM | None: - """Return a llama-index compatible LLM, lazily created on first access. - - Avoids the cost of RetrieverLLM construction (adapter init, - CallbackManager setup) for retrievers that never use the LLM - (Simple, Automerging, Recursive). - """ - if self._llm is None: - return None - if self._retriever_llm is None: - self._retriever_llm = RetrieverLLM(llm=self._llm) - return self._retriever_llm - - def require_llm(self) -> RetrieverLLM: - """Return the llama-index LLM or raise if not configured. - - Call this in retrievers that need an LLM (KeywordTable, Fusion, - Subquestion) to fail early with a clear message instead of - letting llama-index silently fall back to its default OpenAI LLM. - """ - llm = self.llm - if llm is None: - raise ValueError( - f"{type(self).__name__} requires an LLM. " - "Pass llm= when constructing the retriever." - ) - return llm - - @staticmethod - def retrieve() -> set[str]: - return set() diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/fusion.py b/prompt-service/src/unstract/prompt_service/core/retrievers/fusion.py deleted file mode 100644 index 5fd7870f01..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/fusion.py +++ /dev/null @@ -1,95 +0,0 @@ -import logging - -from llama_index.core import VectorStoreIndex -from llama_index.core.retrievers import QueryFusionRetriever -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class FusionRetriever(BaseRetriever): - """Fusion retrieval class using LlamaIndex's native QueryFusionRetriever. - - This technique generates multiple query variations and combines results - using reciprocal rank fusion for improved relevance. - """ - - def retrieve(self) -> set[str]: - """Retrieve text chunks using LlamaIndex's QueryFusionRetriever. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - llm = self.require_llm() - logger.info( - f"Retrieving chunks for {self.doc_id} using LlamaIndex QueryFusionRetriever." - ) - - # Get the vector store index - vector_store_index: VectorStoreIndex = self.vector_db.get_vector_store_index() - - # Create multiple retrievers with different parameters for true fusion - filters = MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ) - - # Retriever 1: Standard similarity search - retriever_1 = vector_store_index.as_retriever( - similarity_top_k=self.top_k, - filters=filters, - ) - - # Retriever 2: Broader search with more candidates - retriever_2 = vector_store_index.as_retriever( - similarity_top_k=self.top_k * 2, - filters=filters, - ) - - # Retriever 3: Focused search with fewer candidates - retriever_3 = vector_store_index.as_retriever( - similarity_top_k=max(1, self.top_k // 2), - filters=filters, - ) - - # Create LlamaIndex QueryFusionRetriever with multiple retrievers - fusion_retriever = QueryFusionRetriever( - [retriever_1, retriever_2, retriever_3], # Multiple retrievers for fusion - similarity_top_k=self.top_k, - num_queries=4, # Generate multiple query variations - mode="simple", # Use simple fusion mode (reciprocal rank fusion) - use_async=False, - verbose=True, - llm=llm, - ) - - # Retrieve nodes using fusion technique - nodes = fusion_retriever.retrieve(self.prompt) - - # Extract unique text chunks - chunks: set[str] = set() - for node in nodes: - if node.score > 0: - chunks.add(node.get_content()) - else: - logger.info( - f"Node score is less than 0. " - f"Ignored: {node.node_id} with score {node.score}" - ) - - logger.info(f"Successfully retrieved {len(chunks)} chunks using fusion.") - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during fusion retrieval for {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error( - f"Unexpected error during fusion retrieval for {self.doc_id}: {e}" - ) - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/keyword_table.py b/prompt-service/src/unstract/prompt_service/core/retrievers/keyword_table.py deleted file mode 100644 index d17e507b50..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/keyword_table.py +++ /dev/null @@ -1,80 +0,0 @@ -import logging - -from llama_index.core import VectorStoreIndex -from llama_index.core.indices.keyword_table import KeywordTableIndex -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class KeywordTableRetriever(BaseRetriever): - """Keyword table retrieval using LlamaIndex's native KeywordTableIndex.""" - - def retrieve(self) -> set[str]: - """Retrieve text chunks using LlamaIndex's native KeywordTableIndex. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - llm = self.require_llm() - logger.info( - f"Retrieving chunks for {self.doc_id} using LlamaIndex KeywordTableIndex." - ) - - # Get documents from vector index for keyword indexing - vector_store_index: VectorStoreIndex = self.vector_db.get_vector_store_index() - - # Get all nodes for the document - all_retriever = vector_store_index.as_retriever( - similarity_top_k=1000, # Get all nodes - filters=MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ), - ) - - # Retrieve all nodes to build keyword index - all_nodes = all_retriever.retrieve(" ") - - if not all_nodes: - logger.warning(f"No nodes found for doc_id: {self.doc_id}") - return set() - - # Create KeywordTableIndex from nodes using our provided LLM - keyword_index = KeywordTableIndex( - nodes=[node.node for node in all_nodes], - show_progress=True, - llm=llm, - ) - - # Create retriever from keyword index - keyword_retriever = keyword_index.as_retriever( - similarity_top_k=self.top_k, - ) - - # Retrieve nodes using keyword matching - nodes = keyword_retriever.retrieve(self.prompt) - - # Extract unique text chunks - chunks: set[str] = set() - for node in nodes: - chunks.add(node.get_content()) - - logger.info( - f"Successfully retrieved {len(chunks)} chunks using KeywordTableIndex." - ) - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during keyword retrieval for {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error( - f"Unexpected error during keyword retrieval for {self.doc_id}: {e}" - ) - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/recursive.py b/prompt-service/src/unstract/prompt_service/core/retrievers/recursive.py deleted file mode 100644 index dcec3d6176..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/recursive.py +++ /dev/null @@ -1,77 +0,0 @@ -import logging - -from llama_index.core import VectorStoreIndex -from llama_index.core.retrievers import RecursiveRetriever -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class RecursiveRetrieval(BaseRetriever): - """Recursive retrieval using LlamaIndex's native RecursiveRetriever. - - This retriever performs recursive retrieval by breaking down queries - and refining results through multiple retrieval steps. - """ - - def retrieve(self) -> set[str]: - """Retrieve text chunks using LlamaIndex's native RecursiveRetriever. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - logger.info( - f"Retrieving chunks for {self.doc_id} using LlamaIndex RecursiveRetriever." - ) - - # Get the vector store index - vector_store_index: VectorStoreIndex = self.vector_db.get_vector_store_index() - - # Create base retriever with metadata filters - base_retriever = vector_store_index.as_retriever( - similarity_top_k=self.top_k, - filters=MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ), - ) - - # Create RecursiveRetriever - recursive_retriever = RecursiveRetriever( - "vector", # root retriever key - retriever_dict={"vector": base_retriever}, - verbose=True, - ) - - # Retrieve nodes using RecursiveRetriever - nodes = recursive_retriever.retrieve(self.prompt) - - # Extract unique text chunks - chunks: set[str] = set() - for node in nodes: - if node.score > 0: - chunks.add(node.get_content()) - else: - logger.info( - f"Node score is less than 0. " - f"Ignored: {node.node_id} with score {node.score}" - ) - - logger.info( - f"Successfully retrieved {len(chunks)} chunks using RecursiveRetriever." - ) - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during recursive retrieval for {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error( - f"Unexpected error during recursive retrieval for {self.doc_id}: {e}" - ) - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/retriever_llm.py b/prompt-service/src/unstract/prompt_service/core/retrievers/retriever_llm.py deleted file mode 100644 index c2038f9181..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/retriever_llm.py +++ /dev/null @@ -1,126 +0,0 @@ -from collections.abc import Sequence -from typing import Any - -from llama_index.core.base.llms.types import ( - ChatMessage, - ChatResponse, - ChatResponseAsyncGen, - ChatResponseGen, - CompletionResponse, - CompletionResponseAsyncGen, - CompletionResponseGen, - LLMMetadata, - MessageRole, -) -from llama_index.core.llms.llm import LLM as LlamaIndexBaseLLM # noqa: N811 -from pydantic import PrivateAttr - -from unstract.sdk1.llm import LLM, LLMCompat - - -class RetrieverLLM(LlamaIndexBaseLLM): - """Bridges SDK1's LLMCompat with llama-index's LLM for retriever use. - - Llama-index's ``resolve_llm()`` asserts ``isinstance(llm, LLM)`` - where ``LLM`` is ``llama_index.core.llms.llm.LLM``. Since SDK1's - ``LLMCompat`` is a plain class without llama-index inheritance, - it fails this check. - - ``RetrieverLLM`` inherits from llama-index's ``LLM`` base class - (passing the isinstance check) and delegates all LLM calls to an - internal ``LLMCompat`` instance. - """ - - _compat: LLMCompat = PrivateAttr() - - def __init__(self, llm: LLM, **kwargs: Any) -> None: # noqa: ANN401 - """Initialize with an SDK1 LLM instance.""" - super().__init__(**kwargs) - self._compat = LLMCompat.from_llm(llm) - - @property - def metadata(self) -> LLMMetadata: - return LLMMetadata( - is_chat_model=True, - model_name=self._compat.get_model_name(), - ) - - # ── Sync ───────────────────────────────────────────────────────────────── - - def chat( - self, - messages: Sequence[ChatMessage], - **kwargs: Any, # noqa: ANN401 - ) -> ChatResponse: - result = self._compat.chat(messages, **kwargs) - return ChatResponse( - message=ChatMessage( - role=MessageRole.ASSISTANT, - content=result.message.content, - ), - raw=result.raw, - ) - - def complete( - self, - prompt: str, - formatted: bool = False, - **kwargs: Any, # noqa: ANN401 - ) -> CompletionResponse: - result = self._compat.complete(prompt, formatted=formatted, **kwargs) - return CompletionResponse(text=result.text, raw=result.raw) - - def stream_chat( - self, - messages: Sequence[ChatMessage], - **kwargs: Any, # noqa: ANN401 - ) -> ChatResponseGen: - raise NotImplementedError("stream_chat is not supported.") - - def stream_complete( - self, - prompt: str, - formatted: bool = False, - **kwargs: Any, # noqa: ANN401 - ) -> CompletionResponseGen: - raise NotImplementedError("stream_complete is not supported.") - - # ── Async ──────────────────────────────────────────────────────────────── - - async def achat( - self, - messages: Sequence[ChatMessage], - **kwargs: Any, # noqa: ANN401 - ) -> ChatResponse: - result = await self._compat.achat(messages, **kwargs) - return ChatResponse( - message=ChatMessage( - role=MessageRole.ASSISTANT, - content=result.message.content, - ), - raw=result.raw, - ) - - async def acomplete( - self, - prompt: str, - formatted: bool = False, - **kwargs: Any, # noqa: ANN401 - ) -> CompletionResponse: - result = await self._compat.acomplete(prompt, formatted=formatted, **kwargs) - return CompletionResponse(text=result.text, raw=result.raw) - - async def astream_chat( - self, - messages: Sequence[ChatMessage], - **kwargs: Any, # noqa: ANN401 - ) -> ChatResponseAsyncGen: - raise NotImplementedError("astream_chat is not supported.") - - async def astream_complete( - self, - prompt: str, - formatted: bool = False, - **kwargs: Any, # noqa: ANN401 - ) -> CompletionResponseAsyncGen: - raise NotImplementedError("astream_complete is not supported.") diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/router.py b/prompt-service/src/unstract/prompt_service/core/retrievers/router.py deleted file mode 100644 index b5bc4efe1d..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/router.py +++ /dev/null @@ -1,157 +0,0 @@ -import logging - -from llama_index.core import VectorStoreIndex -from llama_index.core.query_engine import RouterQueryEngine -from llama_index.core.selectors import LLMSingleSelector -from llama_index.core.tools import QueryEngineTool, ToolMetadata -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class RouterRetriever(BaseRetriever): - """Router retrieval class using LlamaIndex's native RouterQueryEngine. - - This technique intelligently routes queries to different retrieval strategies - based on query analysis. - """ - - def _create_metadata_filters(self): - """Create metadata filters for doc_id.""" - return MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ) - - def _create_base_query_engine(self, vector_store_index, filters): - """Create the base vector query engine.""" - return vector_store_index.as_query_engine( - similarity_top_k=self.top_k, - filters=filters, - llm=self.llm, - ) - - def _add_keyword_search_tool(self, query_engine_tools, vector_store_index, filters): - """Add keyword search tool to query engine tools list.""" - try: - keyword_query_engine = vector_store_index.as_query_engine( - similarity_top_k=self.top_k * 2, - filters=filters, - llm=self.llm, - ) - query_engine_tools.append( - QueryEngineTool( - query_engine=keyword_query_engine, - metadata=ToolMetadata( - name="keyword_search", - description=( - "Best for finding specific terms, names, numbers, dates, " - "or exact phrases. Use when looking for precise matches." - ), - ), - ) - ) - except Exception as e: - logger.debug(f"Could not create keyword search engine: {e}") - - def _add_broad_search_tool(self, query_engine_tools, vector_store_index, filters): - """Add broad search tool to query engine tools list.""" - try: - broad_query_engine = vector_store_index.as_query_engine( - similarity_top_k=self.top_k * 3, - filters=filters, - llm=self.llm, - ) - query_engine_tools.append( - QueryEngineTool( - query_engine=broad_query_engine, - metadata=ToolMetadata( - name="broad_search", - description=( - "Useful for general questions, exploratory queries, " - "or when you need comprehensive information on a topic." - ), - ), - ) - ) - except Exception as e: - logger.debug(f"Could not create broad search engine: {e}") - - def _extract_chunks_from_response(self, response): - """Extract chunks from router query response.""" - chunks: set[str] = set() - if hasattr(response, "source_nodes"): - for node in response.source_nodes: - if node.score > 0: - chunks.add(node.get_content()) - else: - logger.info( - f"Node score is less than 0. " - f"Ignored: {node.node_id} with score {node.score}" - ) - return chunks - - def retrieve(self) -> set[str]: - """Retrieve text chunks using LlamaIndex's RouterQueryEngine. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - logger.info( - f"Retrieving chunks for {self.doc_id} using LlamaIndex RouterQueryEngine." - ) - - vector_store_index: VectorStoreIndex = self.vector_db.get_vector_store_index() - filters = self._create_metadata_filters() - vector_query_engine = self._create_base_query_engine( - vector_store_index, filters - ) - - if not self.llm: - return set() - - # Create base query engine tools - query_engine_tools = [ - QueryEngineTool( - query_engine=vector_query_engine, - metadata=ToolMetadata( - name="vector_search", - description=( - "Useful for semantic similarity search, conceptual questions, " - "and finding information based on meaning and context." - ), - ), - ), - ] - - # Add additional search strategies - self._add_keyword_search_tool(query_engine_tools, vector_store_index, filters) - self._add_broad_search_tool(query_engine_tools, vector_store_index, filters) - - # Create and execute router query - router_query_engine = RouterQueryEngine.from_defaults( - selector=LLMSingleSelector.from_defaults(llm=self.llm), - query_engine_tools=query_engine_tools, - verbose=True, - llm=self.llm, - ) - - response = router_query_engine.query(self.prompt) - chunks = self._extract_chunks_from_response(response) - - logger.info(f"Successfully retrieved {len(chunks)} chunks using router.") - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during router retrieval for {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error( - f"Unexpected error during router retrieval for {self.doc_id}: {e}" - ) - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/simple.py b/prompt-service/src/unstract/prompt_service/core/retrievers/simple.py deleted file mode 100644 index 15f25db1bb..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/simple.py +++ /dev/null @@ -1,51 +0,0 @@ -import time - -from flask import current_app as app -from llama_index.core import VectorStoreIndex -from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever - - -class SimpleRetriever(BaseRetriever): - def retrieve(self) -> set[str]: - context = self._simple_retrieval() - if not context: - # UN-1288 For Pinecone, we are seeing an inconsistent case where - # query with doc_id fails even though indexing just happened. - # This causes the following retrieve to return no text. - # To rule out any lag on the Pinecone vector DB write, - # the following sleep is added - # Note: This will not fix the issue. Since this issue is inconsistent - # and not reproducible easily, this is just a safety net. - app.logger.info( - f"[doc_id: {self.doc_id}] Could not retrieve context, " - "retrying after 2 secs to handle issues due to lag" - ) - time.sleep(2) - context = self._simple_retrieval() - return context - - def _simple_retrieval(self): - vector_query_engine: VectorStoreIndex = self.vector_db.get_vector_store_index() - retriever = vector_query_engine.as_retriever( - similarity_top_k=self.top_k, - filters=MetadataFilters( - filters=[ - ExactMatchFilter(key="doc_id", value=self.doc_id), - ], - ), - ) - nodes = retriever.retrieve(self.prompt) - context: set[str] = set() - for node in nodes: - # May have to fine-tune this value for node score or keep it - # configurable at the adapter level - if node.score > 0: - context.add(node.get_content()) - else: - app.logger.info( - "Node score is less than 0. " - f"Ignored: {node.node_id} with score {node.score}" - ) - return context diff --git a/prompt-service/src/unstract/prompt_service/core/retrievers/subquestion.py b/prompt-service/src/unstract/prompt_service/core/retrievers/subquestion.py deleted file mode 100644 index 6930bc131b..0000000000 --- a/prompt-service/src/unstract/prompt_service/core/retrievers/subquestion.py +++ /dev/null @@ -1,65 +0,0 @@ -import logging - -from llama_index.core.query_engine import SubQuestionQueryEngine -from llama_index.core.question_gen.llm_generators import LLMQuestionGenerator -from llama_index.core.schema import QueryBundle -from llama_index.core.tools import QueryEngineTool, ToolMetadata - -from unstract.prompt_service.core.retrievers.base_retriever import BaseRetriever -from unstract.prompt_service.exceptions import RetrievalError - -logger = logging.getLogger(__name__) - - -class SubquestionRetriever(BaseRetriever): - """SubquestionRetrieval class for querying VectorDB using LlamaIndex's - SubQuestionQueryEngine. - """ - - def retrieve(self) -> set[str]: - """Retrieve text chunks from the VectorDB based on the provided prompt. - - Returns: - set[str]: A set of text chunks retrieved from the database. - """ - try: - llm = self.require_llm() - logger.info("Initialising vector query engine...") - vector_query_engine = self.vector_db.get_vector_store_index().as_query_engine( - llm=llm, similarity_top_k=self.top_k - ) - logger.info( - f"Retrieving chunks for {self.doc_id} using SubQuestionQueryEngine." - ) - query_engine_tools = [ - QueryEngineTool( - query_engine=vector_query_engine, - metadata=ToolMetadata( - name=self.doc_id, description=f"Nodes for {self.doc_id}" - ), - ), - ] - query_bundle = QueryBundle(query_str=self.prompt) - - question_gen = LLMQuestionGenerator.from_defaults( - llm=llm, - ) - query_engine = SubQuestionQueryEngine.from_defaults( - query_engine_tools=query_engine_tools, - question_gen=question_gen, - use_async=True, - llm=llm, - ) - - response = query_engine.query(str_or_query_bundle=query_bundle) - - chunks: set[str] = {node.text for node in response.source_nodes} - logger.info(f"Successfully retrieved {len(chunks)} chunks.") - return chunks - - except (ValueError, AttributeError, KeyError, ImportError) as e: - logger.error(f"Error during retrieving chunks {self.doc_id}: {e}") - raise RetrievalError(str(e)) from e - except Exception as e: - logger.error(f"Unexpected error during retrieving chunks {self.doc_id}: {e}") - raise RetrievalError(f"Unexpected error: {str(e)}") from e diff --git a/prompt-service/src/unstract/prompt_service/dto.py b/prompt-service/src/unstract/prompt_service/dto.py deleted file mode 100644 index 8c9e4f3d3c..0000000000 --- a/prompt-service/src/unstract/prompt_service/dto.py +++ /dev/null @@ -1,39 +0,0 @@ -from dataclasses import dataclass, field -from typing import Any - - -@dataclass -class InstanceIdentifiers: - embedding_instance_id: str - vector_db_instance_id: str - x2text_instance_id: str - llm_instance_id: str - tool_id: str - tags: list[str] | None = None - - -@dataclass -class FileInfo: - file_path: str - file_hash: str - - -@dataclass -class ChunkingConfig: - chunk_size: int - chunk_overlap: int - - def __post_init__(self) -> None: - if self.chunk_size == 0: - raise ValueError( - "Indexing cannot be done for zero chunks." - "Please provide a valid chunk_size." - ) - - -@dataclass -class ProcessingOptions: - reindex: bool = False - enable_highlight: bool = False - enable_word_confidence: bool = False - usage_kwargs: dict[Any, Any] = field(default_factory=dict) diff --git a/prompt-service/src/unstract/prompt_service/exceptions.py b/prompt-service/src/unstract/prompt_service/exceptions.py deleted file mode 100644 index 98634ed5fd..0000000000 --- a/prompt-service/src/unstract/prompt_service/exceptions.py +++ /dev/null @@ -1,56 +0,0 @@ -from unstract.core.flask.exceptions import APIError - - -class BadRequest(APIError): - code = 400 - message = "Bad Request / No payload" - - -class RateLimitError(APIError): - code = 429 - message = "Running into rate limit errors, please try again later" - - -class MissingFieldError(APIError): - """Custom error for missing fields.""" - - def __init__(self, missing_fields: list[str]): - message = f"Missing required fields: {', '.join(missing_fields)}" - super().__init__(message=message) - - -class RetrievalError(APIError): - """Custom exception raised for errors during retrieval from VectorDB.""" - - DEFAULT_MESSAGE = ( - "Error while retrieving data from the VectorDB. " - "Please contact the admin for further assistance." - ) - - -class ExtractionError(APIError): - DEFAULT_MESSAGE = "Error while extracting from a document" - - -class UnprocessableEntity(APIError): - code = 422 - message = "Unprocessable Entity" - - -class CustomDataError(APIError): - """Custom exception raised for errors with custom_data variables.""" - - code = 400 - - def __init__(self, variable: str, reason: str, is_ide: bool = True): - if is_ide: - help_text = "Please define this key in Prompt Studio Settings > Custom Data." - else: - help_text = ( - "Please include this key in the 'custom_data' field of your API request." - ) - variable_display = "{{custom_data." + variable + "}}" - message = ( - f"Custom data error for variable '{variable_display}': {reason} {help_text}" - ) - super().__init__(message=message) diff --git a/prompt-service/src/unstract/prompt_service/extensions.py b/prompt-service/src/unstract/prompt_service/extensions.py deleted file mode 100644 index 1626591dc0..0000000000 --- a/prompt-service/src/unstract/prompt_service/extensions.py +++ /dev/null @@ -1,36 +0,0 @@ -from collections.abc import Generator -from contextlib import contextmanager -from os import environ as env -from typing import Any - -from playhouse.postgres_ext import PostgresqlExtDatabase - -from unstract.prompt_service.utils.env_loader import get_env_or_die - -# Load required environment variables -db_host = get_env_or_die("PG_BE_HOST") -db_port = get_env_or_die("PG_BE_PORT") -db_user = get_env_or_die("PG_BE_USERNAME") -db_pass = get_env_or_die("PG_BE_PASSWORD") -db_name = get_env_or_die("PG_BE_DATABASE") -application_name = env.get("APPLICATION_NAME", "unstract-prompt-service") - -# Initialize and connect to the database -db = PostgresqlExtDatabase( - database=db_name, - user=db_user, - host=db_host, - password=db_pass, - port=db_port, - options=f"-c application_name={application_name}", -) - - -@contextmanager -def db_context() -> Generator[PostgresqlExtDatabase, Any, None]: - try: - db.connect() - yield db - finally: - if not db.is_closed(): - db.close() diff --git a/prompt-service/src/unstract/prompt_service/helpers/__init__.py b/prompt-service/src/unstract/prompt_service/helpers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prompt-service/src/unstract/prompt_service/helpers/auth.py b/prompt-service/src/unstract/prompt_service/helpers/auth.py deleted file mode 100644 index cd9fd58128..0000000000 --- a/prompt-service/src/unstract/prompt_service/helpers/auth.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any - -from flask import Request, request -from flask import current_app as app - -from unstract.prompt_service.constants import DBTableV2 -from unstract.prompt_service.extensions import db, db_context -from unstract.prompt_service.utils.db_utils import DBUtils -from unstract.prompt_service.utils.env_loader import get_env_or_die - -DB_SCHEMA = get_env_or_die("DB_SCHEMA", "unstract") - - -class AuthHelper: - @staticmethod - def validate_bearer_token(token: str | None) -> bool: - try: - if token is None: - app.logger.error("Authentication failed. Empty bearer token") - return False - - platform_key_table = f'"{DB_SCHEMA}".{DBTableV2.PLATFORM_KEY}' - with db_context(): - query = f"SELECT * FROM {platform_key_table} WHERE key = %s" - cursor = db.execute_sql(query, (token,)) - result_row = cursor.fetchone() - cursor.close() - if not result_row or len(result_row) == 0: - app.logger.error(f"Authentication failed. bearer token not found {token}") - return False - platform_key = str(result_row[1]) - is_active = bool(result_row[2]) - if not is_active: - app.logger.error( - f"Token is not active. Activate \ - before using it. token {token}" - ) - return False - if platform_key != token: - app.logger.error(f"Authentication failed. Invalid bearer token: {token}") - return False - - except Exception as e: - app.logger.error( - f"Error while validating bearer token: {e}", - stack_info=True, - exc_info=True, - ) - return False - return True - - @staticmethod - def get_token_from_auth_header(request: Request) -> str | None: - try: - bearer_token = request.headers.get("Authorization") - if not bearer_token: - return None - token: str = bearer_token.strip().replace("Bearer ", "") - return token - except Exception as e: - app.logger.info(f"Exception while getting token {e}") - return None - - @staticmethod - def get_account_from_bearer_token(token: str | None) -> str: - platform_key_table = DBTableV2.PLATFORM_KEY - organization_table = DBTableV2.ORGANIZATION - - query = f"SELECT organization_id FROM {platform_key_table} WHERE key=%s" - organization = DBUtils.execute_query(query, (token,)) - query_org = f"SELECT schema_name FROM {organization_table} WHERE id=%s" - schema_name: str = DBUtils.execute_query(query_org, (organization,)) - return schema_name - - @staticmethod - def auth_required(func: Any) -> Any: - def wrapper(*args: Any, **kwargs: Any) -> Any: - token = AuthHelper.get_token_from_auth_header(request) - # Check if bearer token exists and validate it - if not token or not AuthHelper.validate_bearer_token(token): - return "Unauthorized", 401 - request.token = token - return func(*args, **kwargs) - - return wrapper diff --git a/prompt-service/src/unstract/prompt_service/helpers/postprocessor.py b/prompt-service/src/unstract/prompt_service/helpers/postprocessor.py deleted file mode 100644 index 1e7aad72d6..0000000000 --- a/prompt-service/src/unstract/prompt_service/helpers/postprocessor.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -import logging -from typing import Any - -import requests - -logger = logging.getLogger(__name__) - - -def _validate_structured_output(data: Any) -> bool: - """Validate that structured output is a dict or list.""" - return isinstance(data, (dict, list)) - - -def _validate_highlight_data(updated_data: Any, original_data: Any) -> Any: - """Validate highlight data and return appropriate value.""" - if ( - updated_data is not None - and updated_data != original_data - and not isinstance(updated_data, list) - ): - logger.warning( - "Ignoring webhook highlight_data due to invalid type (expected list)" - ) - return original_data - return updated_data - - -def _process_successful_response( - response_data: dict, parsed_data: dict, highlight_data: list | None -) -> tuple[dict[str, Any], list | None]: - """Process successful webhook response.""" - if "structured_output" not in response_data: - logger.warning("Response missing 'structured_output' key") - return parsed_data, highlight_data - - updated_parsed_data = response_data["structured_output"] - - if not _validate_structured_output(updated_parsed_data): - logger.warning("Ignoring postprocessing due to invalid structured_output type") - return parsed_data, highlight_data - - updated_highlight_data = response_data.get("highlight_data", highlight_data) - updated_highlight_data = _validate_highlight_data( - updated_highlight_data, highlight_data - ) - - return updated_parsed_data, updated_highlight_data - - -def _make_webhook_request( - webhook_url: str, payload: dict, timeout: float -) -> tuple[dict[str, Any], list | None] | None: - """Make webhook request and return processed response or None on failure.""" - try: - response = requests.post( - webhook_url, - json=payload, - timeout=timeout, - headers={"Content-Type": "application/json"}, - allow_redirects=False, # Prevent redirect-based SSRF - ) - - if response.status_code != 200: - logger.warning( - f"Postprocessing server returned status code: {response.status_code}" - ) - return None - - return response.json() - - except json.JSONDecodeError as e: - logger.warning(f"Invalid JSON response from postprocessing server: {e}") - except requests.exceptions.Timeout: - logger.warning(f"Postprocessing server request timed out after {timeout}s") - except requests.exceptions.RequestException as e: - logger.warning(f"Postprocessing server request failed: {e}") - except Exception as e: - logger.warning(f"Unexpected error during postprocessing: {e}") - - return None - - -def postprocess_data( - parsed_data: dict[str, Any], - webhook_enabled: bool = False, - webhook_url: str | None = None, - timeout: float = 2.0, - highlight_data: list | None = None, -) -> tuple[dict[str, Any], list | None]: - """Post-process parsed data by sending it to an external server. - - Args: - parsed_data: The parsed data to be post-processed - webhook_enabled: Whether webhook postprocessing is enabled - webhook_url: URL endpoint for the webhook - timeout: Request timeout in seconds (default: 2.0) - highlight_data: Highlight data from metadata to send to webhook - - Returns: - tuple: (postprocessed_data, updated_highlight_data) if successful, otherwise (original_parsed_data, original_highlight_data) - """ - if not webhook_enabled or not webhook_url: - return parsed_data, highlight_data - - payload = {"structured_output": parsed_data} - if highlight_data is not None: - payload["highlight_data"] = highlight_data - - response_data = _make_webhook_request(webhook_url, payload, timeout) - if response_data is None: - return parsed_data, highlight_data - - return _process_successful_response(response_data, parsed_data, highlight_data) diff --git a/prompt-service/src/unstract/prompt_service/helpers/prompt_ide_base_tool.py b/prompt-service/src/unstract/prompt_service/helpers/prompt_ide_base_tool.py deleted file mode 100644 index e2a30a0e2e..0000000000 --- a/prompt-service/src/unstract/prompt_service/helpers/prompt_ide_base_tool.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import warnings - -from flask import current_app - -from unstract.prompt_service.constants import PromptServiceConstants -from unstract.sdk1.constants import LogLevel -from unstract.sdk1.tool.stream import StreamMixin -from unstract.sdk1.utils.common import PY_TO_UNSTRACT_LOG_LEVEL - - -class PromptServiceBaseTool(StreamMixin): - # Remove unused log_level arg - def __init__(self, log_level: LogLevel | None = None, platform_key: str = "") -> None: - """Args: - tool (UnstractAbstractTool): Instance of UnstractAbstractTool. - Deprecated, will be removed in future versions - Notes: - - PLATFORM_SERVICE_API_KEY environment variable is required. - """ - if log_level: - warnings.warn( - "The 'log_level' argument is deprecated and will be " - "removed in a future version.", - DeprecationWarning, - stacklevel=2, # ensures the warning points to the caller of the method - ) - self.log_level = PY_TO_UNSTRACT_LOG_LEVEL.get( - current_app.logger.getEffectiveLevel(), LogLevel.INFO - ) - self.platform_key = platform_key - super().__init__(log_level=self.log_level) - - def get_env_or_die(self, env_key: str) -> str: - """Returns the value of an env variable. - - If it is None, raises an error and exits - - Args: - env_key (str): Key to retrieve - - Returns: - str: Value of the env - """ - # HACK: Adding platform key for multitenancy - if env_key == PromptServiceConstants.PLATFORM_SERVICE_API_KEY: - if not self.platform_key: - current_app.logger.error(f"{env_key} is required") - else: - key: str = self.platform_key - return key - else: - env_value = os.environ.get(env_key) - if env_value is None: - current_app.logger.error(f"Env variable {env_key} is required") - return env_value # type:ignore - return "" diff --git a/prompt-service/src/unstract/prompt_service/helpers/usage.py b/prompt-service/src/unstract/prompt_service/helpers/usage.py deleted file mode 100644 index 63a14c4336..0000000000 --- a/prompt-service/src/unstract/prompt_service/helpers/usage.py +++ /dev/null @@ -1,140 +0,0 @@ -import logging -import traceback -from logging import Logger -from typing import Any - -from flask import current_app as app - -from unstract.prompt_service.constants import DBTableV2 -from unstract.prompt_service.extensions import db, db_context -from unstract.prompt_service.utils.db_utils import DBUtils -from unstract.prompt_service.utils.env_loader import get_env_or_die -from unstract.sdk1.audit import Audit - -logger = logging.getLogger(__name__) - - -class UsageHelper: - @staticmethod - def query_usage_metadata(token: str, metadata: dict[str, Any]) -> dict[str, Any]: - DB_SCHEMA = get_env_or_die("DB_SCHEMA", "unstract") - organization_uid, org_id = DBUtils.get_organization_from_bearer_token(token) - run_id: str = metadata["run_id"] - query: str = f""" - SELECT - usage_type, - llm_usage_reason, - model_name, - SUM(prompt_tokens) AS input_tokens, - SUM(completion_tokens) AS output_tokens, - SUM(total_tokens) AS total_tokens, - SUM(embedding_tokens) AS embedding_tokens, - SUM(cost_in_dollars) AS cost_in_dollars - FROM "{DB_SCHEMA}"."{DBTableV2.TOKEN_USAGE}" - WHERE run_id = %s and organization_id = %s - GROUP BY usage_type, llm_usage_reason, model_name; - """ - logger: Logger = app.logger - try: - logger.info( - "Querying usage metadata for org_id: %s, run_id: %s", org_id, run_id - ) - with db_context(): - with db.execute_sql(query, (run_id, organization_uid)) as cursor: - results: list[tuple] = cursor.fetchall() - # Process results as needed - for row in results: - key, item = UsageHelper._get_key_and_item(row) - # Initialize the key as an empty list if it doesn't exist - if key not in metadata: - metadata[key] = [] - # Append the item to the list associated with the key - metadata[key].append(item) - except Exception as e: - logger.error(f"Error while querying usage metadata: {e}") - return metadata - - @staticmethod - def _get_key_and_item(row: tuple) -> tuple[str, dict[str, Any]]: - ( - usage_type, - llm_usage_reason, - model_name, - input_tokens, - output_tokens, - total_tokens, - embedding_tokens, - cost_in_dollars, - ) = row - cost_in_dollars: str = UsageHelper._format_float_positional(cost_in_dollars) - key: str = usage_type - item: dict[str, Any] = { - "model_name": model_name, - "cost_in_dollars": cost_in_dollars, - } - if llm_usage_reason: - key = f"{llm_usage_reason}_{key}" - item["input_tokens"] = input_tokens - item["output_tokens"] = output_tokens - item["total_tokens"] = total_tokens - else: - item["embedding_tokens"] = embedding_tokens - return key, item - - @staticmethod - def _format_float_positional(value: float, precision: int = 10) -> str: - formatted: str = f"{value:.{precision}f}" - return formatted.rstrip("0").rstrip(".") if "." in formatted else formatted - - @staticmethod - def push_usage_data( - event_type: str, - kwargs: dict[str, Any], - platform_api_key: str, - token_counter=None, - model_name: str = "", - ) -> bool: - """Push usage data to the audit service. - - Args: - event_type: Type of usage event being recorded - kwargs: Additional data to include with the event - platform_api_key: API key for authentication with the audit service - token_counter: Token counter object with token usage metrics - model_name: Name of the model used (if applicable) - - Returns: - bool: True if successful, False otherwise - Note: - This method handles all exceptions internally and returns False on failure - rather than propagating exceptions to the caller. - """ - if not kwargs or not isinstance(kwargs, dict): - logger.error("Invalid kwargs provided to push_usage_data") - return False - - if not platform_api_key or not isinstance(platform_api_key, str): - logger.error("Invalid platform_api_key provided to push_usage_data") - return False - - try: - logger.debug( - f"Pushing usage data for event_type: {event_type}, model: {model_name}" - ) - - # Call the Audit SDK with the appropriate parameters - Audit().push_usage_data( - platform_api_key=platform_api_key, - token_counter=token_counter, - model_name=model_name, - event_type=event_type, - kwargs=kwargs, - ) - - logger.info(f"Successfully pushed usage data for {model_name}") - return True - except Exception as e: - logger.exception( - f"Error pushing usage data: {str(e)} - {traceback.format_exc()}" - ) - return False diff --git a/prompt-service/src/unstract/prompt_service/helpers/variable_replacement.py b/prompt-service/src/unstract/prompt_service/helpers/variable_replacement.py deleted file mode 100644 index cd794ce590..0000000000 --- a/prompt-service/src/unstract/prompt_service/helpers/variable_replacement.py +++ /dev/null @@ -1,204 +0,0 @@ -import json -import re -from functools import lru_cache -from typing import Any - -from flask import current_app as app - -from unstract.prompt_service.constants import VariableConstants, VariableType -from unstract.prompt_service.exceptions import CustomDataError -from unstract.prompt_service.utils.request import HTTPMethod, make_http_request - - -class VariableReplacementHelper: - @staticmethod - def replace_static_variable( - prompt: str, structured_output: dict[str, Any], variable: str - ) -> str: - output_value = VariableReplacementHelper.check_static_variable_run_status( - structure_output=structured_output, variable=variable - ) - if not output_value: - return prompt - static_variable_marker_string = "".join(["{{", variable, "}}"]) - - replaced_prompt: str = VariableReplacementHelper.replace_generic_string_value( - prompt=prompt, variable=static_variable_marker_string, value=output_value - ) - - return replaced_prompt - - @staticmethod - def check_static_variable_run_status( - structure_output: dict[str, Any], variable: str - ) -> Any: - output = None - try: - output = structure_output[variable] - except KeyError: - app.logger.warning( - f"Prompt with {variable} is not executed yet." - " Unable to replace the variable" - ) - return output - - @staticmethod - def replace_generic_string_value(prompt: str, variable: str, value: Any) -> str: - formatted_value: str = value - if not isinstance(value, str): - formatted_value = VariableReplacementHelper.handle_json_and_str_types(value) - replaced_prompt = prompt.replace(variable, formatted_value) - return replaced_prompt - - @staticmethod - def handle_json_and_str_types(value: Any) -> str: - try: - formatted_value = json.dumps(value) - except ValueError: - formatted_value = str(value) - return formatted_value - - @staticmethod - def identify_variable_type(variable: str) -> VariableType: - variable_type: VariableType - - # Check for custom_data variable type first - custom_data_pattern = re.compile(VariableConstants.CUSTOM_DATA_VARIABLE_REGEX) - if re.findall(custom_data_pattern, variable): - variable_type = VariableType.CUSTOM_DATA - else: - # Check for dynamic variable type - dynamic_pattern = re.compile(VariableConstants.DYNAMIC_VARIABLE_URL_REGEX) - if re.findall(dynamic_pattern, variable): - variable_type = VariableType.DYNAMIC - else: - variable_type = VariableType.STATIC - return variable_type - - @staticmethod - def replace_dynamic_variable( - prompt: str, variable: str, structured_output: dict[str, Any] - ) -> str: - url = re.search(VariableConstants.DYNAMIC_VARIABLE_URL_REGEX, variable).group(0) - data = re.findall(VariableConstants.DYNAMIC_VARIABLE_DATA_REGEX, variable)[0] - output_value = VariableReplacementHelper.check_static_variable_run_status( - structure_output=structured_output, variable=data - ) - if not output_value: - return prompt - api_response: Any = VariableReplacementHelper.fetch_dynamic_variable_value( - url=url, data=output_value - ) - formatted_api_response: str = VariableReplacementHelper.handle_json_and_str_types( - api_response - ) - static_variable_marker_string = "".join(["{{", variable, "}}"]) - replaced_prompt: str = VariableReplacementHelper.replace_generic_string_value( - prompt=prompt, - variable=static_variable_marker_string, - value=formatted_api_response, - ) - return replaced_prompt - - @staticmethod - def replace_custom_data_variable( - prompt: str, - variable: str, - custom_data: dict[str, Any], - is_ide: bool = True, - ) -> str: - """Replace custom_data variable in prompt. - - Args: - prompt: The prompt containing variables - variable: The variable to replace (e.g., "custom_data.name") - custom_data: The custom_data data dictionary - is_ide: Whether this is running from Prompt Studio IDE (affects error messages) - - Returns: - prompt with variable replaced - """ - # Extract the path from custom_data.path.to.value - custom_data_match = re.search( - VariableConstants.CUSTOM_DATA_VARIABLE_REGEX, variable - ) - if not custom_data_match: - error_msg = "Invalid variable format." - app.logger.error(f"{error_msg}: {variable}") - raise CustomDataError(variable=variable, reason=error_msg, is_ide=is_ide) - - path_str = custom_data_match.group(1) - path_parts = path_str.split(".") - - if not custom_data: - error_msg = "Custom data is not configured." - app.logger.error(error_msg) - raise CustomDataError(variable=path_str, reason=error_msg, is_ide=is_ide) - - # Navigate through the nested dictionary - try: - value = custom_data - for part in path_parts: - value = value[part] - except (KeyError, TypeError) as e: - error_msg = f"Key '{path_str}' not found in custom data." - app.logger.error(error_msg) - raise CustomDataError( - variable=path_str, reason=error_msg, is_ide=is_ide - ) from e - - # Replace in prompt - let replace_generic_string_value handle formatting - # (it only applies json.dumps for non-string values) - variable_marker_string = "".join(["{{", variable, "}}"]) - - replaced_prompt = VariableReplacementHelper.replace_generic_string_value( - prompt=prompt, - variable=variable_marker_string, - value=value, - ) - - return replaced_prompt - - @staticmethod - @lru_cache(maxsize=128) - def _extract_variables_cached(prompt_text: str) -> tuple[str, ...]: - """Internal cached extraction - returns tuple for lru_cache compatibility.""" - return tuple(re.findall(VariableConstants.VARIABLE_REGEX, prompt_text)) - - @staticmethod - def extract_variables_from_prompt(prompt_text: str) -> list[str]: - """Extract variables from prompt with caching and stats logging. - - Uses lru_cache internally and logs cache statistics periodically - to help determine if caching is beneficial. - """ - result = VariableReplacementHelper._extract_variables_cached(prompt_text) - - # Log stats periodically (every 50 calls) - info_after = VariableReplacementHelper._extract_variables_cached.cache_info() - total_calls = info_after.hits + info_after.misses - - if total_calls % 50 == 0 and total_calls > 0: - hit_rate = info_after.hits / total_calls * 100 - app.logger.info( - f"[VariableCache] total={total_calls} hits={info_after.hits} " - f"misses={info_after.misses} hit_rate={hit_rate:.1f}% " - f"size={info_after.currsize}/{info_after.maxsize} " - f"prompt_chars={len(prompt_text)}" - ) - - return list(result) - - @staticmethod - def fetch_dynamic_variable_value(url: str, data: str) -> Any: - # This prototype method currently supports - # only endpoints that do not require authentication. - # Additionally, it only accepts plain text - # inputs for POST requests in this version. - # Future versions may include support for - # authentication and other input formats. - - verb: HTTPMethod = HTTPMethod.POST - headers = {"Content-Type": "text/plain"} - response: Any = make_http_request(verb=verb, url=url, data=data, headers=headers) - return response diff --git a/prompt-service/src/unstract/prompt_service/run.py b/prompt-service/src/unstract/prompt_service/run.py deleted file mode 100644 index b50e972506..0000000000 --- a/prompt-service/src/unstract/prompt_service/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from unstract.prompt_service.config import create_app - -app = create_app() diff --git a/prompt-service/src/unstract/prompt_service/services/__init__.py b/prompt-service/src/unstract/prompt_service/services/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prompt-service/src/unstract/prompt_service/services/answer_prompt.py b/prompt-service/src/unstract/prompt_service/services/answer_prompt.py deleted file mode 100644 index 9f8cbf9c28..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/answer_prompt.py +++ /dev/null @@ -1,521 +0,0 @@ -import ipaddress -import socket -from logging import Logger -from typing import Any -from urllib.parse import urlparse - -from flask import current_app as app - -from unstract.core.flask import PluginManager -from unstract.core.flask.exceptions import APIError -from unstract.prompt_service.constants import ExecutionSource, FileStorageKeys, RunLevel -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.exceptions import RateLimitError -from unstract.prompt_service.helpers.postprocessor import postprocess_data -from unstract.prompt_service.utils.env_loader import get_env_or_die -from unstract.prompt_service.utils.json_repair_helper import ( - repair_json_with_best_structure, -) -from unstract.prompt_service.utils.log import publish_log -from unstract.sdk1.constants import LogLevel -from unstract.sdk1.exceptions import RateLimitError as SdkRateLimitError -from unstract.sdk1.exceptions import SdkError -from unstract.sdk1.file_storage import FileStorage, FileStorageProvider -from unstract.sdk1.file_storage.constants import StorageType -from unstract.sdk1.file_storage.env_helper import EnvHelper -from unstract.sdk1.llm import LLM - - -def _is_safe_public_url(url: str) -> bool: - """Validate webhook URL for SSRF protection. - - Only allows HTTPS and blocks private/loopback/internal addresses. - Resolves all DNS records (A/AAAA) to prevent DNS rebinding attacks. - """ - try: - p = urlparse(url) - if p.scheme not in ("https",): # Only allow HTTPS for security - return False - host = p.hostname or "" - # Block obvious local hosts - if host in ("localhost",): - return False - - addrs: set[str] = set() - # If literal IP, validate directly; else resolve all records (A/AAAA) - try: - ipaddress.ip_address(host) - addrs.add(host) - except ValueError: - try: - for family, _type, _proto, _canonname, sockaddr in socket.getaddrinfo( - host, None, type=socket.SOCK_STREAM - ): - addr = sockaddr[0] - addrs.add(addr) - except Exception: - return False - - if not addrs: - return False - - # Validate all resolved addresses - for addr in addrs: - try: - ip = ipaddress.ip_address(addr) - except ValueError: - return False - if ( - ip.is_private - or ip.is_loopback - or ip.is_link_local - or ip.is_reserved - or ip.is_multicast - ): - return False - return True - except Exception: - return False - - -class AnswerPromptService: - @staticmethod - def extract_variable( - structured_output: dict[str, Any], - variable_names: list[Any], - output: dict[str, Any], - promptx: str, - ) -> str: - logger: Logger = app.logger - for variable_name in variable_names: - if promptx.find(f"%{variable_name}%") >= 0: - if variable_name in structured_output: - promptx = promptx.replace( - f"%{variable_name}%", - str(structured_output[variable_name]), - ) - else: - raise ValueError( - f"Variable {variable_name} not found in structured output" - ) - - if promptx != output[PSKeys.PROMPT]: - logger.info(f"Prompt after variable replacement: {promptx}") - return promptx - - @staticmethod - def construct_and_run_prompt( - tool_settings: dict[str, Any], - output: dict[str, Any], - llm: LLM, - context: str, - prompt: str, - metadata: dict[str, Any], - file_path: str = "", - execution_source: str | None = ExecutionSource.IDE.value, - ) -> str: - platform_postamble = tool_settings.get(PSKeys.PLATFORM_POSTAMBLE, "") - word_confidence_postamble = tool_settings.get( - PSKeys.WORD_CONFIDENCE_POSTAMBLE, "" - ) - summarize_as_source = tool_settings.get(PSKeys.SUMMARIZE_AS_SOURCE) - enable_highlight = tool_settings.get(PSKeys.ENABLE_HIGHLIGHT, False) - enable_word_confidence = tool_settings.get(PSKeys.ENABLE_WORD_CONFIDENCE, False) - # Dependency: enable_word_confidence only works if enable_highlight is enabled - if not enable_highlight: - enable_word_confidence = False - prompt_type = output.get(PSKeys.TYPE, PSKeys.TEXT) - if not enable_highlight or summarize_as_source: - platform_postamble = "" - if not enable_word_confidence or summarize_as_source: - word_confidence_postamble = "" - plugin = PluginManager().get_plugin("json-extraction") - if plugin and hasattr(plugin["entrypoint_cls"], "update_settings"): - plugin["entrypoint_cls"].update_settings(tool_settings, output) - prompt = AnswerPromptService.construct_prompt( - preamble=tool_settings.get(PSKeys.PREAMBLE, ""), - prompt=output[prompt], - postamble=tool_settings.get(PSKeys.POSTAMBLE, ""), - grammar_list=tool_settings.get(PSKeys.GRAMMAR, []), - context=context, - platform_postamble=platform_postamble, - word_confidence_postamble=word_confidence_postamble, - prompt_type=prompt_type, - ) - output[PSKeys.COMBINED_PROMPT] = prompt - return AnswerPromptService.run_completion( - llm=llm, - prompt=prompt, - metadata=metadata, - prompt_key=output[PSKeys.NAME], - prompt_type=prompt_type, - enable_highlight=enable_highlight, - enable_word_confidence=enable_word_confidence, - file_path=file_path, - execution_source=execution_source, - ) - - @staticmethod - def construct_prompt( - preamble: str, - prompt: str, - postamble: str, - grammar_list: list[dict[str, Any]], - context: str, - platform_postamble: str, - word_confidence_postamble: str, - prompt_type: str = PSKeys.TEXT, - ) -> str: - prompt = f"{preamble}\n\nQuestion or Instruction: {prompt}" - if grammar_list is not None and len(grammar_list) > 0: - prompt += "\n" - for grammar in grammar_list: - word = "" - synonyms = [] - if PSKeys.WORD in grammar: - word = grammar[PSKeys.WORD] - if PSKeys.SYNONYMS in grammar: - synonyms = grammar[PSKeys.SYNONYMS] - if len(synonyms) > 0 and word != "": - prompt += ( - f"\nNote: You can consider that the word '{word}' " - f"is the same as {', '.join(synonyms)} in both the question and the context." - ) # noqa - if prompt_type == PSKeys.JSON: - json_postamble = get_env_or_die( - PSKeys.JSON_POSTAMBLE, PSKeys.DEFAULT_JSON_POSTAMBLE - ) - postamble += f"\n{json_postamble}" - if platform_postamble: - platform_postamble += "\n\n" - if word_confidence_postamble: - platform_postamble += f"{word_confidence_postamble}\n\n" - prompt += ( - f"\n\n{postamble}\n\nContext:\n---------------\n{context}\n" - f"-----------------\n\n{platform_postamble}Answer:" - ) - return prompt - - @staticmethod - def run_completion( - llm: LLM, - prompt: str, - metadata: dict[str, str] | None = None, - prompt_key: str | None = None, - prompt_type: str | None = PSKeys.TEXT, - enable_highlight: bool = False, - enable_word_confidence: bool = False, - file_path: str = "", - execution_source: str | None = None, - ) -> str: - logger: Logger = app.logger - try: - highlight_data_plugin: dict[str, Any] = PluginManager().get_plugin( - PSKeys.HIGHLIGHT_DATA_PLUGIN - ) - highlight_data = None - if highlight_data_plugin and enable_highlight: - fs_instance: FileStorage = FileStorage(FileStorageProvider.LOCAL) - if execution_source == ExecutionSource.IDE.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.PERMANENT, - env_name=FileStorageKeys.PERMANENT_REMOTE_STORAGE, - ) - if execution_source == ExecutionSource.TOOL.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.SHARED_TEMPORARY, - env_name=FileStorageKeys.TEMPORARY_REMOTE_STORAGE, - ) - logger.info( - f"Initializing highlight plugin with: " - f"file_path={file_path}, " - f"execution_source={execution_source}, " - f"fs_instance={fs_instance}" - ) - highlight_data = highlight_data_plugin["entrypoint_cls"]( - file_path=file_path, - fs_instance=fs_instance, - enable_word_confidence=enable_word_confidence, - ).run - completion = llm.complete( - prompt=prompt, - process_text=highlight_data, - extract_json=prompt_type.lower() != PSKeys.TEXT, - ) - answer: str = completion[PSKeys.RESPONSE].text - highlight_data = completion.get(PSKeys.HIGHLIGHT_DATA, []) - confidence_data = completion.get(PSKeys.CONFIDENCE_DATA) - word_confidence_data = completion.get(PSKeys.WORD_CONFIDENCE_DATA) - line_numbers = completion.get(PSKeys.LINE_NUMBERS, []) - whisper_hash = completion.get(PSKeys.WHISPER_HASH, "") - if metadata is not None and prompt_key: - metadata.setdefault(PSKeys.HIGHLIGHT_DATA, {})[prompt_key] = ( - highlight_data - ) - metadata.setdefault(PSKeys.LINE_NUMBERS, {})[prompt_key] = line_numbers - metadata[PSKeys.WHISPER_HASH] = whisper_hash - if confidence_data: - metadata.setdefault(PSKeys.CONFIDENCE_DATA, {})[prompt_key] = ( - confidence_data - ) - if enable_word_confidence and word_confidence_data: - metadata.setdefault(PSKeys.WORD_CONFIDENCE_DATA, {})[prompt_key] = ( - word_confidence_data - ) - return answer - except SdkRateLimitError as e: - raise RateLimitError(f"Rate limit error. {str(e)}") from e - except SdkError as e: - logger.error(f"Error fetching response for prompt: {e}.") - # Preserve the status code from the SDK error for proper HTTP response - status_code = getattr(e, "status_code", None) or 500 - raise APIError(message=str(e), code=status_code) from e - - @staticmethod - def extract_table( - output: dict[str, Any], - structured_output: dict[str, Any], - llm: LLM, - execution_source: str, - prompt: str, - ) -> dict[str, Any]: - table_settings = output[PSKeys.TABLE_SETTINGS] - - # Check if prompt has valid schema data using json_repair - has_valid_schema = False - schema_data = None - - if prompt and isinstance(prompt, str): - try: - # Try to repair and parse the prompt as JSON - schema_data = repair_json_with_best_structure(prompt) - # Check if the result is a valid dict (schema object) - if isinstance(schema_data, dict) and schema_data: - has_valid_schema = True - app.logger.info( - "Valid schema detected in prompt, using Smart Table Extractor" - ) - except Exception as e: - app.logger.debug(f"Prompt does not contain valid schema: {e}") - - # If we have a valid schema, use the smart table extractor - if has_valid_schema: - smart_table_plugin: dict[str, Any] = PluginManager().get_plugin( - "smart-table-extractor" - ) - - if smart_table_plugin: - fs_instance = AnswerPromptService._get_file_storage_instance( - execution_source - ) - - try: - # Get the input file from table settings - input_file = table_settings.get("input_file") - - # Run the smart table extractor - result = smart_table_plugin["entrypoint_cls"].run( - llm=llm, - table_settings=table_settings, - fs_instance=fs_instance, - prompt=prompt, - input_file=input_file, - ) - - # Extract the data from the result - answer = result.get("data", []) - structured_output[output[PSKeys.NAME]] = answer - - # We do not support summary and eval for table. - # Hence returning the result - return structured_output - except Exception as e: - app.logger.error(f"Smart Table Extractor failed: {e}") - # Fall back to regular table extractor - app.logger.info("Falling back to regular table extractor") - - # Use regular table extractor (original code) - table_extractor: dict[str, Any] = PluginManager().get_plugin("table-extractor") - if not table_extractor: - raise APIError( - "Unable to extract table details. " - "Please contact admin to resolve this issue." - ) - fs_instance = AnswerPromptService._get_file_storage_instance(execution_source) - - try: - answer = table_extractor["entrypoint_cls"].run_table_extraction( - llm=llm, - table_settings=table_settings, - fs_instance=fs_instance, - prompt=prompt, - ) - structured_output[output[PSKeys.NAME]] = answer - # We do not support summary and eval for table. - # Hence returning the result - return structured_output - except table_extractor["exception_cls"] as e: - msg = f"Couldn't extract table. {e}" - raise APIError(message=msg) - - @staticmethod - def _get_file_storage_instance(execution_source) -> FileStorage: - fs_instance: FileStorage = FileStorage(FileStorageProvider.LOCAL) - if execution_source == ExecutionSource.IDE.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.PERMANENT, - env_name=FileStorageKeys.PERMANENT_REMOTE_STORAGE, - ) - if execution_source == ExecutionSource.TOOL.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.SHARED_TEMPORARY, - env_name=FileStorageKeys.TEMPORARY_REMOTE_STORAGE, - ) - - return fs_instance - - @staticmethod - def handle_json( - answer: str, - structured_output: dict[str, Any], - output: dict[str, Any], - log_events_id: str, - tool_id: str, - doc_name: str, - llm: LLM, - enable_highlight: bool = False, - enable_word_confidence: bool = False, - execution_source: str = ExecutionSource.IDE.value, - metadata: dict[str, Any] | None = None, - file_path: str = "", - ) -> None: - """Handle JSON responses from the LLM.""" - prompt_key = output[PSKeys.NAME] - if answer.lower() == "na": - structured_output[prompt_key] = None - else: - # Use the utility function to repair JSON with the best structure - parsed_data = repair_json_with_best_structure(answer) - - if isinstance(parsed_data, str): - err_msg = "Error parsing response to JSON" - app.logger.error(err_msg) - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_key, - "doc_name": doc_name, - }, - LogLevel.WARN, - RunLevel.RUN, - "Unable to parse JSON response from LLM, try using our" - " cloud / enterprise feature 'record' or 'table' type", - ) - structured_output[prompt_key] = {} - else: - # Get webhook configuration from output settings - webhook_enabled = output.get(PSKeys.ENABLE_POSTPROCESSING_WEBHOOK, False) - webhook_url = output.get(PSKeys.POSTPROCESSING_WEBHOOK_URL) - - # Get highlight data from metadata if available and highlighting is enabled - highlight_data = None - if enable_highlight and metadata and PSKeys.HIGHLIGHT_DATA in metadata: - highlight_data = metadata[PSKeys.HIGHLIGHT_DATA].get(prompt_key) - - # Process data (optionally via webhook) with safety guards and fallback - processed_data = parsed_data - updated_highlight_data = None - - if webhook_enabled: - if not webhook_url: - app.logger.warning( - "Postprocessing webhook enabled but URL missing; skipping." - ) - elif not _is_safe_public_url(webhook_url): - app.logger.warning( - "Postprocessing webhook URL is not allowed; skipping." - ) - else: - try: - processed_data, updated_highlight_data = postprocess_data( - parsed_data, - webhook_enabled=True, - webhook_url=webhook_url, - highlight_data=highlight_data, - timeout=60, - ) - except Exception as e: - app.logger.warning( - f"Postprocessing webhook failed: {e}. Using unprocessed data." - ) - - structured_output[prompt_key] = processed_data - - # Update metadata with processed highlight data if available and highlighting is enabled - if enable_highlight and metadata and updated_highlight_data is not None: - metadata.setdefault(PSKeys.HIGHLIGHT_DATA, {})[prompt_key] = ( - updated_highlight_data - ) - - @staticmethod - def extract_line_item( - tool_settings: dict[str, Any], - output: dict[str, Any], - structured_output: dict[str, Any], - llm: LLM, - file_path: str, - metadata: dict[str, str] | None, - execution_source: str, - ) -> dict[str, Any]: - line_item_extraction_plugin: dict[str, Any] = PluginManager().get_plugin( - "line-item-extraction" - ) - if not line_item_extraction_plugin: - raise APIError(PSKeys.PAID_FEATURE_MSG) - - extract_file_path = file_path - - # Read file content into context - fs_instance: FileStorage = FileStorage(FileStorageProvider.LOCAL) - if execution_source == ExecutionSource.IDE.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.PERMANENT, - env_name=FileStorageKeys.PERMANENT_REMOTE_STORAGE, - ) - if execution_source == ExecutionSource.TOOL.value: - fs_instance = EnvHelper.get_storage( - storage_type=StorageType.SHARED_TEMPORARY, - env_name=FileStorageKeys.TEMPORARY_REMOTE_STORAGE, - ) - - if not fs_instance.exists(extract_file_path): - raise FileNotFoundError( - f"The file at path '{extract_file_path}' does not exist." - ) - context = fs_instance.read(path=extract_file_path, encoding="utf-8", mode="r") - - prompt = AnswerPromptService.construct_prompt( - preamble=tool_settings.get(PSKeys.PREAMBLE, ""), - prompt=output["promptx"], - postamble=tool_settings.get(PSKeys.POSTAMBLE, ""), - grammar_list=tool_settings.get(PSKeys.GRAMMAR, []), - context=context, - platform_postamble="", - word_confidence_postamble="", - ) - try: - line_item_extraction = line_item_extraction_plugin["entrypoint_cls"]( - llm=llm, - tool_settings=tool_settings, - output=output, - prompt=prompt, - structured_output=structured_output, - ) - answer = line_item_extraction.run() - structured_output[output[PSKeys.NAME]] = answer - metadata[PSKeys.CONTEXT][output[PSKeys.NAME]] = [context] - return structured_output - except line_item_extraction_plugin["exception_cls"] as e: - msg = f"Couldn't extract table. {e}" - raise APIError(message=msg) diff --git a/prompt-service/src/unstract/prompt_service/services/extraction.py b/prompt-service/src/unstract/prompt_service/services/extraction.py deleted file mode 100644 index 76430657f9..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/extraction.py +++ /dev/null @@ -1,92 +0,0 @@ -from pathlib import Path -from typing import Any - -from unstract.prompt_service.constants import ExecutionSource -from unstract.prompt_service.constants import IndexingConstants as IKeys -from unstract.prompt_service.exceptions import ExtractionError -from unstract.prompt_service.helpers.prompt_ide_base_tool import PromptServiceBaseTool -from unstract.prompt_service.utils.file_utils import FileUtils -from unstract.sdk1.adapters.exceptions import AdapterError -from unstract.sdk1.adapters.x2text.constants import X2TextConstants -from unstract.sdk1.adapters.x2text.llm_whisperer.src import LLMWhisperer -from unstract.sdk1.adapters.x2text.llm_whisperer_v2.src import LLMWhispererV2 -from unstract.sdk1.utils.common import log_elapsed -from unstract.sdk1.utils.tool import ToolUtils -from unstract.sdk1.x2txt import TextExtractionResult, X2Text - - -class ExtractionService: - @staticmethod - @log_elapsed(operation="EXTRACTION") - def perform_extraction( - x2text_instance_id: str, - file_path: str, - run_id: str, - platform_key: str, - output_file_path: str | None = None, - enable_highlight: bool = False, - usage_kwargs: dict[Any, Any] = {}, - tags: list[str] | None = None, - execution_source: str | None = None, - tool_exec_metadata: dict[str, Any] | None = None, - execution_run_data_folder: str | None = None, - ) -> str: - extracted_text = "" - util = PromptServiceBaseTool(platform_key=platform_key) - x2text = X2Text( - tool=util, adapter_instance_id=x2text_instance_id, usage_kwargs=usage_kwargs - ) - fs = FileUtils.get_fs_instance(execution_source=execution_source) - try: - if enable_highlight and ( - isinstance(x2text.x2text_instance, LLMWhisperer) - or isinstance(x2text.x2text_instance, LLMWhispererV2) - ): - process_response: TextExtractionResult = x2text.process( - input_file_path=file_path, - output_file_path=output_file_path, - enable_highlight=enable_highlight, - tags=tags, - fs=fs, - ) - ExtractionService.update_exec_metadata( - fs, - execution_source, - tool_exec_metadata, - execution_run_data_folder, - process_response, - ) - else: - process_response: TextExtractionResult = x2text.process( - input_file_path=file_path, - output_file_path=output_file_path, - tags=tags, - fs=fs, - ) - extracted_text = process_response.extracted_text - return extracted_text - except AdapterError as e: - msg = f"Error from text extractor '{x2text.x2text_instance.get_name()}'. " - msg += str(e) - code = e.status_code if e.status_code != -1 else 500 - raise ExtractionError(msg, code=code) from e - - @staticmethod - def update_exec_metadata( - fs, - execution_source, - tool_exec_metadata, - execution_run_data_folder, - process_response, - ): - if execution_source == ExecutionSource.TOOL.value: - whisper_hash_value = process_response.extraction_metadata.whisper_hash - metadata = {X2TextConstants.WHISPER_HASH: whisper_hash_value} - for key, value in metadata.items(): - tool_exec_metadata[key] = value - metadata_path = str(Path(execution_run_data_folder) / IKeys.METADATA_FILE) - ToolUtils.dump_json( - file_to_dump=metadata_path, - json_to_dump=metadata, - fs=fs, - ) diff --git a/prompt-service/src/unstract/prompt_service/services/indexing.py b/prompt-service/src/unstract/prompt_service/services/indexing.py deleted file mode 100644 index b71a4f9897..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/indexing.py +++ /dev/null @@ -1,94 +0,0 @@ -import logging - -from unstract.prompt_service.core.index_v2 import Index -from unstract.prompt_service.dto import ( - ChunkingConfig, - FileInfo, - InstanceIdentifiers, - ProcessingOptions, -) -from unstract.prompt_service.exceptions import APIError -from unstract.prompt_service.helpers.prompt_ide_base_tool import PromptServiceBaseTool -from unstract.prompt_service.utils.file_utils import FileUtils -from unstract.sdk1.embedding import EmbeddingCompat -from unstract.sdk1.utils.indexing import IndexingUtils -from unstract.sdk1.vector_db import VectorDB - -logger = logging.getLogger(__name__) - - -class IndexingService: - @staticmethod - def index( - execution_source: str, - chunking_config: ChunkingConfig, - file_info: FileInfo, - instance_identifiers: InstanceIdentifiers, - processing_options: ProcessingOptions, - platform_key: str, - run_id: str, - extracted_text: str, - ) -> str: - try: - fs_instance = FileUtils.get_fs_instance(execution_source=execution_source) - util = PromptServiceBaseTool(platform_key=platform_key) - index: Index = Index( - tool=util, - run_id=run_id, - capture_metrics=True, - instance_identifiers=instance_identifiers, - chunking_config=chunking_config, - processing_options=processing_options, - ) - doc_id: str = IndexingUtils.generate_index_key( - vector_db=instance_identifiers.vector_db_instance_id, - embedding=instance_identifiers.embedding_instance_id, - file_path=file_info.file_path, - file_hash=file_info.file_hash, - x2text=instance_identifiers.x2text_instance_id, - chunk_size=str(chunking_config.chunk_size), - chunk_overlap=str(chunking_config.chunk_overlap), - tool=util, - fs=fs_instance, - ) - - # Skip embedding creation and indexing when chunk_size is 0 - # When chunk_size is 0, we don't need vector operations - if chunking_config.chunk_size == 0: - logger.info(f"Skipping indexing for chunk_size=0. Doc ID: {doc_id}") - return doc_id - - embedding = EmbeddingCompat( - adapter_instance_id=instance_identifiers.embedding_instance_id, - tool=util, - kwargs={ - **processing_options.usage_kwargs, - }, - ) - - vector_db = VectorDB( - tool=util, - adapter_instance_id=instance_identifiers.vector_db_instance_id, - embedding=embedding, - ) - - doc_id_found = index.is_document_indexed( - doc_id=doc_id, - embedding=embedding, - vector_db=vector_db, - ) - - # Index and return doc_id - index.perform_indexing( - vector_db=vector_db, - doc_id=doc_id, - extracted_text=extracted_text, - doc_id_found=doc_id_found, - ) - return doc_id - except Exception as e: - status_code = getattr(e, "status_code", 500) - raise APIError(f"Error while indexing: {str(e)}", code=status_code) from e - finally: - if "vector_db" in locals(): - vector_db.close() diff --git a/prompt-service/src/unstract/prompt_service/services/rentrolls_extractor/interface.py b/prompt-service/src/unstract/prompt_service/services/rentrolls_extractor/interface.py deleted file mode 100644 index 4bf6914211..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/rentrolls_extractor/interface.py +++ /dev/null @@ -1,70 +0,0 @@ -import logging -import os -from typing import Any - -import requests - -logger = logging.getLogger(__name__) - - -class RentRollExtractor: - """Main class for rent roll extraction.""" - - def __init__(self): - """Initialize the rent roll extractor.""" - logger.info("Initialized RentRollExtractor") - pass - - def process( - self, - extractor_settings: dict[str, Any], - extracted_data: str, - llm_config: dict[str, Any], - schema: str, - ) -> dict[str, Any]: - """Process the extraction with the given settings. - - Args: - extractor_settings: Dictionary containing extraction settings. - extracted_data: Extracted data from the document. - llm_config: Dictionary containing LLM configuration. - schema: Schema for the extraction. - - Returns: - Dict containing the extraction results. - """ - logger.info("Starting rent roll extraction process") - try: - # Load environment variables for rent roll service - rentroll_host = os.environ.get("RENTROLL_SERVICE_HOST", "http://localhost") - rentroll_port = os.environ.get("RENTROLL_SERVICE_PORT", "5003") - rentroll_url = f"{rentroll_host}:{rentroll_port}/api/extract-rentrolls" - - # Prepare the request payload - payload = { - "text": extracted_data, - "settings": extractor_settings, - "llm_config": llm_config, - "schema": schema, - } - - # Make HTTP call to rent roll service - logger.info(f"Calling rent roll service at: {rentroll_url}") - response = requests.post(rentroll_url, json=payload) - - if response.status_code == 200: - result = response.json() - - logger.info("Successfully received response from rent roll service") - return result - else: - error_text = response.text - logger.error( - f"Rent roll service returned status {response.status_code}: {error_text}" - ) - raise Exception( - f"Rent roll service error: {response.status_code} - {error_text}" - ) - except Exception as e: - logger.error(f"Error in rent roll extraction: {str(e)}", exc_info=True) - raise diff --git a/prompt-service/src/unstract/prompt_service/services/retrieval.py b/prompt-service/src/unstract/prompt_service/services/retrieval.py deleted file mode 100644 index 92ba46c930..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/retrieval.py +++ /dev/null @@ -1,156 +0,0 @@ -import datetime -from typing import Any - -from flask import current_app as app - -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.constants import RetrievalStrategy -from unstract.prompt_service.core.retrievers.automerging import AutomergingRetriever -from unstract.prompt_service.core.retrievers.fusion import FusionRetriever -from unstract.prompt_service.core.retrievers.keyword_table import KeywordTableRetriever -from unstract.prompt_service.core.retrievers.recursive import RecursiveRetrieval -from unstract.prompt_service.core.retrievers.router import RouterRetriever -from unstract.prompt_service.core.retrievers.simple import SimpleRetriever -from unstract.prompt_service.core.retrievers.subquestion import SubquestionRetriever -from unstract.prompt_service.services.answer_prompt import AnswerPromptService -from unstract.prompt_service.utils.file_utils import FileUtils -from unstract.prompt_service.utils.metrics import Metrics -from unstract.sdk1.llm import LLM -from unstract.sdk1.vector_db import VectorDB - - -class RetrievalService: - @staticmethod - def perform_retrieval( # type:ignore - tool_settings: dict[str, Any], - output: dict[str, Any], - doc_id: str, - llm: LLM, - vector_db: VectorDB, - retrieval_type: str, - metadata: dict[str, Any], - chunk_size: int, - execution_source: str, - file_path: str, - context_retrieval_metrics: dict[str, Any], - ) -> tuple[str, list[str]]: - prompt_name = output.get(PSKeys.NAME, "") - vector_db_id = ( - getattr(vector_db, "_adapter_instance_id", None) if vector_db else None - ) - app.logger.info( - f"[Retrieval] prompt='{prompt_name}' doc_id={doc_id} " - f"chunk_size={chunk_size} method={'complete_context' if chunk_size == 0 else 'chunked'}" - + (f" vector_db={vector_db_id}" if vector_db_id else "") - ) - - context: list[str] - if chunk_size == 0: - context = RetrievalService.retrieve_complete_context( - execution_source=execution_source, - file_path=file_path, - context_retrieval_metrics=context_retrieval_metrics, - prompt_key=prompt_name, - ) - else: - context = RetrievalService.run_retrieval( - output=output, - doc_id=doc_id, - llm=llm, - vector_db=vector_db, - retrieval_type=retrieval_type, - context_retrieval_metrics=context_retrieval_metrics, - ) - answer = AnswerPromptService.construct_and_run_prompt( # type:ignore - tool_settings=tool_settings, - output=output, - llm=llm, - context="\n".join(context), - prompt="promptx", - metadata=metadata, - execution_source=execution_source, - file_path=file_path, - ) - return answer, context - - @staticmethod - def run_retrieval( # type:ignore - output: dict[str, Any], - doc_id: str, - llm: LLM, - vector_db: VectorDB, - retrieval_type: str, - context_retrieval_metrics: dict[str, Any], - ) -> list[str]: - context: set[str] - prompt = output[PSKeys.PROMPTX] - top_k = output[PSKeys.SIMILARITY_TOP_K] - prompt_key = output[PSKeys.NAME] - retrieval_start_time = datetime.datetime.now() - - # Map retrieval type to retriever class - retriever_map = { - RetrievalStrategy.SIMPLE.value: SimpleRetriever, - RetrievalStrategy.SUBQUESTION.value: SubquestionRetriever, - RetrievalStrategy.FUSION.value: FusionRetriever, - RetrievalStrategy.RECURSIVE.value: RecursiveRetrieval, - RetrievalStrategy.ROUTER.value: RouterRetriever, - RetrievalStrategy.KEYWORD_TABLE.value: KeywordTableRetriever, - RetrievalStrategy.AUTOMERGING.value: AutomergingRetriever, - } - - # Get the appropriate retriever class - retriever_class = retriever_map.get(retrieval_type) - if not retriever_class: - raise ValueError(f"Unknown retrieval type: {retrieval_type}") - - # Create and execute retriever - retriever = retriever_class( - vector_db=vector_db, - doc_id=doc_id, - prompt=prompt, - top_k=top_k, - llm=llm, - ) - context = retriever.retrieve() - elapsed = Metrics.elapsed_time(start_time=retrieval_start_time) - context_retrieval_metrics[prompt_key] = {"time_taken(s)": elapsed} - - app.logger.info( - f"[Retrieval] prompt='{prompt_key}' doc_id={doc_id} " - f"strategy='{retrieval_type}' top_k={top_k} chunks={len(context)} time={elapsed:.3f}s" - ) - - return list(context) - - @staticmethod - def retrieve_complete_context( - execution_source: str, - file_path: str, - context_retrieval_metrics: dict[str, Any], - prompt_key: str, - ) -> list[str]: - """Loads full context from raw file for zero chunk size retrieval. - - Args: - execution_source: Source of execution (e.g., "api", "workflow"). - file_path: Path to the extracted text file. - context_retrieval_metrics: Dict to store retrieval timing metrics - (modified in-place). - prompt_key: Name/identifier of the prompt for metrics tracking. - - Returns: - List containing the complete file content as a single string. - """ - fs_instance = FileUtils.get_fs_instance(execution_source=execution_source) - retrieval_start_time = datetime.datetime.now() - context = fs_instance.read(path=file_path, mode="r") - elapsed = Metrics.elapsed_time(start_time=retrieval_start_time) - context_retrieval_metrics[prompt_key] = {"time_taken(s)": elapsed} - - app.logger.info( - f"[Retrieval] prompt='{prompt_key}' complete_context " - f"chars={len(context)} time={elapsed:.3f}s" - ) - - return [context] diff --git a/prompt-service/src/unstract/prompt_service/services/variable_replacement.py b/prompt-service/src/unstract/prompt_service/services/variable_replacement.py deleted file mode 100644 index 41ba342ac2..0000000000 --- a/prompt-service/src/unstract/prompt_service/services/variable_replacement.py +++ /dev/null @@ -1,134 +0,0 @@ -from typing import Any - -from flask import current_app as app - -from unstract.prompt_service.constants import PromptServiceConstants as PSKeys -from unstract.prompt_service.constants import RunLevel, VariableType -from unstract.prompt_service.helpers.variable_replacement import ( - VariableReplacementHelper, -) -from unstract.prompt_service.utils.log import publish_log -from unstract.sdk1.constants import LogLevel - - -class VariableReplacementService: - @staticmethod - def is_variables_present(prompt_text: str) -> bool: - """Determines if variables are present in the prompt. - - Args: - prompt (str): Prompt to check - - Returns: - bool: True if variables are present else False - """ - return bool( - len(VariableReplacementHelper.extract_variables_from_prompt(prompt_text)) - ) - - @staticmethod - def replace_variables_in_prompt( - prompt: dict[str, Any], - structured_output: dict[str, Any], - log_events_id: str, - tool_id: str, - prompt_name: str, - doc_name: str, - custom_data: dict[str, Any] = None, - is_ide: bool = True, - ) -> str: - """Replaces variables in prompt. - - Args: - prompt (dict[str, Any]): Dict representing the prompt card - structured_output (dict[str, Any]): Structured data used for variable - replacement when variable map is not present in `prompt`. - log_events_id (str): UUID for the WS communication - tool_id (str): UUID for the prompt studio project - prompt_name (str): Name of the prompt being run - doc_name (str): Name of the document being run with - Returns: - prompt_text (str): Prompt with variables replaced - """ - app.logger.info(f"[{tool_id}] Replacing variables in prompt : {prompt_name}") - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.RUN, - "Replacing variables in prompt", - ) - # Initialized with the prompt because - # it is used in finally block - prompt_text = prompt[PSKeys.PROMPT] - try: - variable_map = prompt[PSKeys.VARIABLE_MAP] - prompt_text = VariableReplacementService._execute_variable_replacement( - prompt_text=prompt[PSKeys.PROMPT], - variable_map=variable_map, - custom_data=custom_data, - is_ide=is_ide, - ) - except KeyError: - # Executed incase of structured tool and - # APIs where we do not set the variable map - prompt_text = VariableReplacementService._execute_variable_replacement( - prompt_text=prompt_text, - variable_map=structured_output, - custom_data=custom_data, - is_ide=is_ide, - ) - finally: - publish_log( - log_events_id, - { - "tool_id": tool_id, - "prompt_key": prompt_name, - "doc_name": doc_name, - }, - LogLevel.DEBUG, - RunLevel.RUN, - f"Variables replaced in prompt with key :{prompt_name}", - ) - return prompt_text - - @staticmethod - def _execute_variable_replacement( - prompt_text: str, - variable_map: dict[str, Any], - custom_data: dict[str, Any] = None, - is_ide: bool = True, - ) -> str: - variables: list[str] = VariableReplacementHelper.extract_variables_from_prompt( - prompt_text=prompt_text - ) - for variable in variables: - variable_type = VariableReplacementHelper.identify_variable_type( - variable=variable - ) - if variable_type == VariableType.STATIC: - prompt_text = VariableReplacementHelper.replace_static_variable( - prompt=prompt_text, - structured_output=variable_map, - variable=variable, - ) - - elif variable_type == VariableType.DYNAMIC: - prompt_text = VariableReplacementHelper.replace_dynamic_variable( - prompt=prompt_text, - variable=variable, - structured_output=variable_map, - ) - - elif variable_type == VariableType.CUSTOM_DATA: - prompt_text = VariableReplacementHelper.replace_custom_data_variable( - prompt=prompt_text, - variable=variable, - custom_data=custom_data or {}, - is_ide=is_ide, - ) - return prompt_text diff --git a/prompt-service/src/unstract/prompt_service/tests/conftest.py b/prompt-service/src/unstract/prompt_service/tests/conftest.py deleted file mode 100644 index c04fa27d3a..0000000000 --- a/prompt-service/src/unstract/prompt_service/tests/conftest.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest -from dotenv import load_dotenv -from flask import Flask -from flask_wtf.csrf import CSRFProtect - -from unstract.prompt_service.controllers.extraction import extraction_bp -from unstract.prompt_service.controllers.indexing import indexing_bp - -# Load test environment variables -load_dotenv(".env.test") - - -@pytest.fixture -def client(): - app = Flask(__name__) - app.register_blueprint(indexing_bp) - app.register_blueprint(extraction_bp) - app.config.update( - { - "TESTING": True, - "DEBUG": True, - "PROPAGATE_EXCEPTIONS": True, - "WTF_CSRF_ENABLED": False, - } - ) - csrf = CSRFProtect() - csrf.init_app(app) # Compliant - with app.test_client() as client: - yield client diff --git a/prompt-service/src/unstract/prompt_service/tests/integration/input/sample1.pdf b/prompt-service/src/unstract/prompt_service/tests/integration/input/sample1.pdf deleted file mode 100644 index 960486fddba0d3f955c84b19f989405fb28c12fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17597 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 -TEST_EMBEDDING_ID=d -TEST_VECTOR_DB_ID= -TEST_X2TEXT_ID= -TEST_TOOL_ID=mock_tool_id -TEST_FILE_PATH=unstract/prompt-studio-data/sample1.pdf -TEST_CHUNK_SIZE=512 -TEST_CHUNK_OVERLAP=128 -TEST_EXECUTION_SOURCE=ide -TEST_RUN_ID=mock_run_id -TEST_EXTRACTED_TEXT=mock_extracted_text -TEST_TAGS=mock_tag1,mock_tag2 -TEST_SOURCE_FILE_PATH=/unstract/prompt-service/src/unstract/prompt_service/tests/integration/input/sample1.pdf -TEST_OUTPUT_FILE_PATH=/unstract/prompt-service/src/unstract/prompt_service/tests/integration/output/output.txt - -# Platform Service -# Platform service is expected to be up and running for this integration test to run -PLATFORM_SERVICE_HOST=http://127.0.0.1/ -PLATFORM_SERVICE_PORT=3001 - -#Remote storage related envs -PERMANENT_REMOTE_STORAGE='{"provider": "minio", "credentials": {"endpoint_url": "http://localhost:9000", "key": "minio", "secret": "minio123"}}' -TEMPORARY_REMOTE_STORAGE='{"provider": "minio", "credentials": {"endpoint_url": "http://unstract-minio:9000", "key": "minio", "secret": "minio123"}}' -REMOTE_PROMPT_STUDIO_FILE_PATH="unstract/prompt-studio-data/" diff --git a/prompt-service/src/unstract/prompt_service/tests/unit/__init__.py b/prompt-service/src/unstract/prompt_service/tests/unit/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prompt-service/src/unstract/prompt_service/tests/unit/conftest.py b/prompt-service/src/unstract/prompt_service/tests/unit/conftest.py deleted file mode 100644 index b78a6985a1..0000000000 --- a/prompt-service/src/unstract/prompt_service/tests/unit/conftest.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Pytest configuration for unit tests. - -Unit tests should not require external dependencies or the full app. -This conftest intentionally does NOT import Flask app components. - -WARNING: This file is NOT auto-loaded when running via tox because ---noconftest is used to skip the parent tests/conftest.py (which -imports Flask blueprints and triggers the full adapter import chain). -If you add shared fixtures here, either remove --noconftest from -tox.ini and fix the parent conftest's eager imports, or define -fixtures directly in test files. -""" diff --git a/prompt-service/src/unstract/prompt_service/tests/unit/test_retriever_llm.py b/prompt-service/src/unstract/prompt_service/tests/unit/test_retriever_llm.py deleted file mode 100644 index 377988b20c..0000000000 --- a/prompt-service/src/unstract/prompt_service/tests/unit/test_retriever_llm.py +++ /dev/null @@ -1,315 +0,0 @@ -"""Unit tests for RetrieverLLM bridge class and BaseRetriever.llm property.""" - -import sys -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest -from llama_index.core.base.llms.types import ( - ChatMessage, - ChatResponse, - CompletionResponse, - LLMMetadata, - MessageRole, -) -from llama_index.core.llms.llm import LLM as LlamaIndexBaseLLM # noqa: N811 -from unstract.prompt_service.core.retrievers.retriever_llm import RetrieverLLM -from unstract.sdk1.llm import LLM, LLMCompat -from unstract.sdk1.llm import ChatMessage as EmulatedChatMessage -from unstract.sdk1.llm import ChatResponse as EmulatedChatResponse -from unstract.sdk1.llm import CompletionResponse as EmulatedCompletionResponse -from unstract.sdk1.llm import LLMMetadata as EmulatedLLMMetadata -from unstract.sdk1.llm import MessageRole as EmulatedMessageRole - -# ── Fixtures ───────────────────────────────────────────────────────────────── - - -@pytest.fixture -def mock_sdk1_llm(): - """Create a mock SDK1 LLM instance with adapter attributes.""" - llm = MagicMock(spec=LLM) - llm._adapter_id = "openai" - llm._adapter_metadata = {"api_key": "test-key"} - llm._adapter_instance_id = "inst-123" - llm._tool = MagicMock() - llm._usage_kwargs = {"run_id": "test-run"} - llm._capture_metrics = False - llm.get_model_name.return_value = "gpt-4" - llm.platform_kwargs = {"run_id": "test-run"} - return llm - - -@pytest.fixture -def mock_compat(): - """Create a mock LLMCompat instance.""" - compat = MagicMock(spec=LLMCompat) - compat.get_model_name.return_value = "gpt-4" - compat.metadata = EmulatedLLMMetadata( - is_chat_model=True, model_name="gpt-4" - ) - return compat - - -# ── BaseRetriever.llm property tests ──────────────────────────────────────── -# BaseRetriever imports VectorDB which triggers the full adapter registration -# chain (Pinecone, Milvus, etc.). We stub unstract.sdk1.vector_db in -# sys.modules so that BaseRetriever can be imported without those heavy deps. - - -@pytest.fixture -def _stub_vector_db(): - """Temporarily stub VectorDB module for BaseRetriever import.""" - stub = MagicMock() - key = "unstract.sdk1.vector_db" - original = sys.modules.get(key) - sys.modules[key] = stub - # Also ensure the base_retriever module is re-imported with the stub. - mod_key = ( - "unstract.prompt_service.core.retrievers.base_retriever" - ) - sys.modules.pop(mod_key, None) - yield stub - # Restore original module (or remove stub). - if original is not None: - sys.modules[key] = original - else: - sys.modules.pop(key, None) - sys.modules.pop(mod_key, None) - - -@pytest.fixture -def base_retriever_cls(_stub_vector_db): - """Import and return BaseRetriever with VectorDB stubbed.""" - from unstract.prompt_service.core.retrievers.base_retriever import ( - BaseRetriever, - ) - - return BaseRetriever - - -class TestBaseRetrieverLlmProperty: - """Tests for BaseRetriever.llm lazy property.""" - - def test_returns_none_when_no_llm_provided( - self, base_retriever_cls - ): - """llm property should return None when constructed without LLM.""" - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - ) - assert retriever.llm is None - - def test_returns_retriever_llm_instance( - self, base_retriever_cls, mock_sdk1_llm - ): - """llm property should return a RetrieverLLM wrapping SDK1 LLM.""" - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - llm=mock_sdk1_llm, - ) - result = retriever.llm - - assert isinstance(result, RetrieverLLM) - assert isinstance(result, LlamaIndexBaseLLM) - - def test_lazily_creates_retriever_llm( - self, base_retriever_cls, mock_sdk1_llm - ): - """RetrieverLLM should not be created until .llm is accessed.""" - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - llm=mock_sdk1_llm, - ) - # Before accessing .llm, the internal cache should be None. - assert retriever._retriever_llm is None - - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - _ = retriever.llm - - # After access, it should be populated. - assert retriever._retriever_llm is not None - - def test_caches_retriever_llm_across_accesses( - self, base_retriever_cls, mock_sdk1_llm - ): - """Repeated .llm accesses should return the same instance.""" - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - llm=mock_sdk1_llm, - ) - first = retriever.llm - second = retriever.llm - - assert first is second - - def test_from_llm_called_once_on_repeated_access( - self, base_retriever_cls, mock_sdk1_llm - ): - """LLMCompat.from_llm should only be called once across accesses.""" - with patch.object( - LLMCompat, "from_llm", return_value=MagicMock() - ) as mock_factory: - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - llm=mock_sdk1_llm, - ) - _ = retriever.llm - _ = retriever.llm - _ = retriever.llm - - mock_factory.assert_called_once() - - def test_raw_llm_still_accessible( - self, base_retriever_cls, mock_sdk1_llm - ): - """The raw SDK1 LLM should remain accessible via _llm.""" - retriever = base_retriever_cls( - vector_db=MagicMock(), - prompt="test", - doc_id="doc-1", - top_k=5, - llm=mock_sdk1_llm, - ) - assert retriever._llm is mock_sdk1_llm - - -# ── RetrieverLLM tests ─────────────────────────────────────────────────────── - - -class TestRetrieverLLM: - """Tests for the RetrieverLLM bridge class.""" - - def test_isinstance_llama_index_llm(self, mock_sdk1_llm): - """RetrieverLLM must pass llama-index's isinstance check.""" - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - assert isinstance(retriever_llm, LlamaIndexBaseLLM) - - def test_uses_from_llm_factory(self, mock_sdk1_llm): - """RetrieverLLM should use LLMCompat.from_llm, not private attrs.""" - with patch.object( - LLMCompat, "from_llm", return_value=MagicMock() - ) as mock_factory: - RetrieverLLM(llm=mock_sdk1_llm) - mock_factory.assert_called_once_with(mock_sdk1_llm) - - def test_metadata_returns_llama_index_type( - self, mock_sdk1_llm, mock_compat - ): - """Metadata property should return llama-index LLMMetadata.""" - with patch.object(LLMCompat, "from_llm", return_value=mock_compat): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - meta = retriever_llm.metadata - assert isinstance(meta, LLMMetadata) - assert meta.is_chat_model is True - assert meta.model_name == "gpt-4" - - def test_chat_delegates_and_converts_types( - self, mock_sdk1_llm, mock_compat - ): - """chat() should delegate to compat and return llama-index types.""" - mock_compat.chat.return_value = EmulatedChatResponse( - message=EmulatedChatMessage( - role=EmulatedMessageRole.ASSISTANT, - content="Hello from LLM", - ), - raw={"id": "resp-1"}, - ) - with patch.object(LLMCompat, "from_llm", return_value=mock_compat): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - messages = [ - ChatMessage(role=MessageRole.USER, content="Hi") - ] - result = retriever_llm.chat(messages) - - assert isinstance(result, ChatResponse) - assert result.message.content == "Hello from LLM" - assert result.raw == {"id": "resp-1"} - mock_compat.chat.assert_called_once() - - def test_complete_delegates_and_converts_types( - self, mock_sdk1_llm, mock_compat - ): - """complete() should delegate to compat and return llama-index types.""" - mock_compat.complete.return_value = EmulatedCompletionResponse( - text="Completed text", raw={"id": "resp-2"} - ) - with patch.object(LLMCompat, "from_llm", return_value=mock_compat): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - result = retriever_llm.complete("Test prompt") - - assert isinstance(result, CompletionResponse) - assert result.text == "Completed text" - assert result.raw == {"id": "resp-2"} - mock_compat.complete.assert_called_once() - - @pytest.mark.asyncio - async def test_achat_delegates_and_converts_types( - self, mock_sdk1_llm, mock_compat - ): - """achat() should delegate to compat and return llama-index types.""" - mock_compat.achat = AsyncMock( - return_value=EmulatedChatResponse( - message=EmulatedChatMessage( - role=EmulatedMessageRole.ASSISTANT, - content="Async hello", - ), - raw={"id": "resp-3"}, - ) - ) - with patch.object(LLMCompat, "from_llm", return_value=mock_compat): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - messages = [ - ChatMessage(role=MessageRole.USER, content="Hi async") - ] - result = await retriever_llm.achat(messages) - - assert isinstance(result, ChatResponse) - assert result.message.content == "Async hello" - - @pytest.mark.asyncio - async def test_acomplete_delegates_and_converts_types( - self, mock_sdk1_llm, mock_compat - ): - """acomplete() should delegate to compat and return llama-index types.""" - mock_compat.acomplete = AsyncMock( - return_value=EmulatedCompletionResponse( - text="Async completed", raw={"id": "resp-4"} - ) - ) - with patch.object(LLMCompat, "from_llm", return_value=mock_compat): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - result = await retriever_llm.acomplete("Async prompt") - - assert isinstance(result, CompletionResponse) - assert result.text == "Async completed" - - def test_stream_chat_not_implemented(self, mock_sdk1_llm): - """stream_chat() should raise NotImplementedError.""" - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - with pytest.raises(NotImplementedError): - retriever_llm.stream_chat([]) - - def test_stream_complete_not_implemented(self, mock_sdk1_llm): - """stream_complete() should raise NotImplementedError.""" - with patch.object(LLMCompat, "from_llm", return_value=MagicMock()): - retriever_llm = RetrieverLLM(llm=mock_sdk1_llm) - with pytest.raises(NotImplementedError): - retriever_llm.stream_complete("prompt") diff --git a/prompt-service/src/unstract/prompt_service/utils/__init__.py b/prompt-service/src/unstract/prompt_service/utils/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prompt-service/src/unstract/prompt_service/utils/db_utils.py b/prompt-service/src/unstract/prompt_service/utils/db_utils.py deleted file mode 100644 index a350f28061..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/db_utils.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any - -from unstract.prompt_service.constants import DBTableV2 -from unstract.prompt_service.extensions import db, db_context -from unstract.prompt_service.utils.env_loader import get_env_or_die - -DB_SCHEMA = get_env_or_die("DB_SCHEMA", "unstract") - - -class DBUtils: - @classmethod - def get_organization_from_bearer_token(cls, token: str) -> tuple[int | None, str]: - """Retrieve organization ID and identifier using a bearer token. - - Args: - token (str): The bearer token (platform key). - - Returns: - tuple[int, str]: organization uid and organization identifier - """ - platform_key_table = f'"{DB_SCHEMA}".{DBTableV2.PLATFORM_KEY}' - organization_table = f'"{DB_SCHEMA}".{DBTableV2.ORGANIZATION}' - - organization_uid: int | None = cls.execute_query( - f"SELECT organization_id FROM {platform_key_table} WHERE key=%s", (token,) - ) - if organization_uid is None: - return None, None - - organization_identifier: str | None = cls.execute_query( - f"SELECT organization_id FROM {organization_table} WHERE id=%s", - (organization_uid,), - ) - return organization_uid, organization_identifier - - @classmethod - def execute_query(cls, query: str, params: tuple = ()) -> Any: - with db_context(): - cursor = db.execute_sql(query, params) - result_row = cursor.fetchone() - cursor.close() - if not result_row or len(result_row) == 0: - return None - return result_row[0] diff --git a/prompt-service/src/unstract/prompt_service/utils/env_loader.py b/prompt-service/src/unstract/prompt_service/utils/env_loader.py deleted file mode 100644 index 481a3eb34a..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/env_loader.py +++ /dev/null @@ -1,20 +0,0 @@ -import os - - -def get_env_or_die(env_key: str, default: str | None = None) -> str: - """Get the value of an environment variable or raise an error if it is not set. - - Args: - env_key (str): Name of the environment variable. - default (Optional[str]): Default value if the variable is not set. - - Returns: - str: The value of the environment variable. - - Raises: - ValueError: If the variable is not set. - """ - env_value = os.environ.get(env_key, default=default) - if env_value is None: - raise ValueError(f"Env variable {env_key} is required") - return env_value diff --git a/prompt-service/src/unstract/prompt_service/utils/file_utils.py b/prompt-service/src/unstract/prompt_service/utils/file_utils.py deleted file mode 100644 index ceb32acab8..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/file_utils.py +++ /dev/null @@ -1,33 +0,0 @@ -from unstract.prompt_service.constants import ExecutionSource, FileStorageKeys -from unstract.sdk1.file_storage import FileStorage -from unstract.sdk1.file_storage.constants import StorageType -from unstract.sdk1.file_storage.env_helper import EnvHelper - - -class FileUtils: - @staticmethod - def get_fs_instance(execution_source: str) -> FileStorage: - """Returns a FileStorage instance based on the execution source. - - Args: - execution_source (str): The source from which the execution is triggered. - - Returns: - FileStorage: The file storage instance - Permanent/Shared temporary - - Raises: - ValueError: If the execution source is invalid. - """ - if execution_source == ExecutionSource.IDE.value: - return EnvHelper.get_storage( - storage_type=StorageType.PERMANENT, - env_name=FileStorageKeys.PERMANENT_REMOTE_STORAGE, - ) - - if execution_source == ExecutionSource.TOOL.value: - return EnvHelper.get_storage( - storage_type=StorageType.SHARED_TEMPORARY, - env_name=FileStorageKeys.TEMPORARY_REMOTE_STORAGE, - ) - - raise ValueError(f"Invalid execution source: {execution_source}") diff --git a/prompt-service/src/unstract/prompt_service/utils/json_repair_helper.py b/prompt-service/src/unstract/prompt_service/utils/json_repair_helper.py deleted file mode 100644 index c15918dc26..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/json_repair_helper.py +++ /dev/null @@ -1,64 +0,0 @@ -"""JSON repair utility functions.""" - -from typing import Any - -from json_repair import repair_json - - -def repair_json_with_best_structure(json_str: str) -> Any: - """Intelligently repair JSON string using the best parsing strategy. - - This function attempts to parse JSON in two ways: - 1. As-is (could be valid object, array, or partial JSON) - 2. With array wrapping (useful for comma-separated objects) - - It chooses the result based on structural integrity rather than string length. - - Args: - json_str: The JSON string to repair - - Returns: - The parsed JSON object with the best structure - """ - # Attempt parsing as-is - parsed_as_is = repair_json(json_str=json_str, return_objects=True, ensure_ascii=False) - - # Attempt parsing with array wrap - parsed_with_wrap = repair_json( - json_str="[" + json_str, return_objects=True, ensure_ascii=False - ) - - # If both results are strings, return the as-is result - if isinstance(parsed_as_is, str) and isinstance(parsed_with_wrap, str): - return parsed_as_is - - # If only one is a string, return the non-string result - if isinstance(parsed_as_is, str): - return parsed_with_wrap - if isinstance(parsed_with_wrap, str): - return parsed_as_is - - # Both are valid structures - choose based on structure analysis - # If parsed_with_wrap is a list with exactly one element that equals parsed_as_is, - # then the original was already valid and wrapping just added unnecessary array - if ( - isinstance(parsed_with_wrap, list) - and len(parsed_with_wrap) == 1 - and parsed_with_wrap[0] == parsed_as_is - ): - return parsed_as_is - - # If parsed_as_is is a valid structure (dict or list), prefer it - # unless parsed_with_wrap provides a more complete structure - if isinstance(parsed_as_is, (dict, list)): - # Check if the wrapped version provides multiple objects that were - # incorrectly concatenated in the original (e.g., {},{},{}) - if isinstance(parsed_with_wrap, list) and len(parsed_with_wrap) > 1: - # The original likely had multiple comma-separated objects - return parsed_with_wrap - else: - # The original was already a valid structure - return parsed_as_is - - # Default to wrapped version if we can't determine otherwise - return parsed_with_wrap diff --git a/prompt-service/src/unstract/prompt_service/utils/log.py b/prompt-service/src/unstract/prompt_service/utils/log.py deleted file mode 100644 index a6ff8236a6..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/log.py +++ /dev/null @@ -1,26 +0,0 @@ -from unstract.core.pubsub_helper import LogPublisher -from unstract.prompt_service.constants import RunLevel -from unstract.sdk1.constants import LogLevel - - -def publish_log( - log_events_id: str, - component: dict[str, str], - level: LogLevel, - state: RunLevel, - message: str, -) -> None: - """Publishes a log to the web socket. - - Args: - log_events_id (str): UUID for the connection - component (dict[str, str]): Dict of tool_id, doc_name and prompt_name to - log context - level (LogLevel): Log level, one of INFO, WARNING, DEBUG, ERROR - state (RunLevel): Run level, one of EVAL, RUN, CHALLENGE, TABLE_EXTRACTION - message (str): Message to log - """ - LogPublisher.publish( - log_events_id, - LogPublisher.log_prompt(component, level.value, state.value, message), - ) diff --git a/prompt-service/src/unstract/prompt_service/utils/metrics.py b/prompt-service/src/unstract/prompt_service/utils/metrics.py deleted file mode 100644 index b7b419d337..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/metrics.py +++ /dev/null @@ -1,8 +0,0 @@ -import datetime - - -class Metrics: - @staticmethod - def elapsed_time(start_time) -> float: - """Returns the elapsed time since the process was started.""" - return (datetime.datetime.now() - start_time).total_seconds() diff --git a/prompt-service/src/unstract/prompt_service/utils/request.py b/prompt-service/src/unstract/prompt_service/utils/request.py deleted file mode 100644 index 8a5a82d462..0000000000 --- a/prompt-service/src/unstract/prompt_service/utils/request.py +++ /dev/null @@ -1,66 +0,0 @@ -from enum import Enum -from typing import Any - -import requests as pyrequests -from flask import current_app as app -from requests.exceptions import RequestException - -from unstract.prompt_service.exceptions import APIError, BadRequest, MissingFieldError - - -class HTTPMethod(str, Enum): - GET = "GET" - POST = "POST" - DELETE = "DELETE" - PUT = "PUT" - PATCH = "PATCH" - - -def make_http_request( - verb: HTTPMethod, - url: str, - data: dict[str, Any] | None = None, - headers: dict[str, Any] | None = None, - params: dict[str, Any] | None = None, -) -> str: - """Generic helper function to help make a HTTP request.""" - try: - if verb == HTTPMethod.GET: - response = pyrequests.get(url, params=params, headers=headers) - elif verb == HTTPMethod.POST: - response = pyrequests.post(url, json=data, params=params, headers=headers) - elif verb == HTTPMethod.DELETE: - response = pyrequests.delete(url, params=params, headers=headers) - else: - raise ValueError("Invalid HTTP verb. Supported verbs: GET, POST, DELETE") - - response.raise_for_status() - return_val: str = ( - response.json() - if response.headers.get("content-type") == "application/json" - else response.text - ) - return return_val - except RequestException as e: - app.logger.error(f"HTTP request error: {e}") - # Extract status code from requests exception if available - status_code = None - if getattr(e, "response", None) is not None: - status_code = getattr(e.response, "status_code", None) - raise APIError( - f"HTTP {verb.value} request to {url} failed: {e!s}", - code=status_code or 500, - ) from e - except Exception as e: - app.logger.error(f"An unexpected error occurred: {e}") - raise e - - -def validate_request_payload(payload, required_fields): - if not payload: - raise BadRequest() - - # Validate required fields - missing_fields = [field for field in required_fields if field not in payload] - if missing_fields: - raise MissingFieldError(missing_fields) diff --git a/prompt-service/uv.lock b/prompt-service/uv.lock deleted file mode 100644 index 68187468cb..0000000000 --- a/prompt-service/uv.lock +++ /dev/null @@ -1,3025 +0,0 @@ -version = 1 -revision = 2 -requires-python = "==3.12.*" - -[[package]] -name = "adlfs" -version = "2024.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "azure-core" }, - { name = "azure-datalake-store" }, - { name = "azure-identity" }, - { name = "azure-storage-blob" }, - { name = "fsspec" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/1e/6d5146676044247af566fa5843b335b1a647e6446070cec9c8b61c31b369/adlfs-2024.7.0.tar.gz", hash = "sha256:106995b91f0eb5e775bcd5957d180d9a14faef3271a063b1f65c66fd5ab05ddf", size = 48588, upload-time = "2024-07-22T12:10:33.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/51/a71c457bd0bc8af3e522b6999ff300852c7c446e384fd9904b0794f875df/adlfs-2024.7.0-py3-none-any.whl", hash = "sha256:2005c8e124fda3948f2a6abb2dbebb2c936d2d821acaca6afd61932edfa9bc07", size = 41349, upload-time = "2024-07-22T12:10:32.226Z" }, -] - -[[package]] -name = "aiobotocore" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aioitertools" }, - { name = "botocore" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/d2/d7e46bcc4c0b5b8e751092824d6ca9af5928adae0f864336e43c7f7a436a/aiobotocore-2.13.1.tar.gz", hash = "sha256:134f9606c2f91abde38cbc61c3241113e26ff244633e0c31abb7e09da3581c9b", size = 104475, upload-time = "2024-06-24T18:30:36.509Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/07/42f884c1600169e4267575cdd261c75dea31782d8fd877bbea358d559416/aiobotocore-2.13.1-py3-none-any.whl", hash = "sha256:1bef121b99841ee3cc788e4ed97c332ba32353b1f00e886d1beb3aae95520858", size = 76864, upload-time = "2024-06-24T18:30:33.379Z" }, -] - -[package.optional-dependencies] -boto3 = [ - { name = "boto3" }, -] - -[[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.15" -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/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" }, - { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" }, - { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" }, - { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" }, - { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" }, - { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" }, - { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" }, - { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" }, -] - -[[package]] -name = "aioitertools" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/de/38491a84ab323b47c7f86e94d2830e748780525f7a10c8600b67ead7e9ea/aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b", size = 19369, upload-time = "2024-09-02T03:33:40.349Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/13/58b70a580de00893223d61de8fea167877a3aed97d4a5e1405c9159ef925/aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796", size = 24345, upload-time = "2024-09-02T03:34:59.454Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions" }, -] -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 = "aiosqlite" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, -] - -[[package]] -name = "amqp" -version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "vine" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432", size = 129013, upload-time = "2024-11-12T19:55:44.051Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944, upload-time = "2024-11-12T19:55:41.782Z" }, -] - -[[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.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, -] - -[[package]] -name = "asyncpg" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/64/9d3e887bb7b01535fdbc45fbd5f0a8447539833b97ee69ecdbb7a79d0cb4/asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e", size = 673162, upload-time = "2024-10-20T00:29:41.88Z" }, - { url = "https://files.pythonhosted.org/packages/6e/eb/8b236663f06984f212a087b3e849731f917ab80f84450e943900e8ca4052/asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a", size = 637025, upload-time = "2024-10-20T00:29:43.352Z" }, - { url = "https://files.pythonhosted.org/packages/cc/57/2dc240bb263d58786cfaa60920779af6e8d32da63ab9ffc09f8312bd7a14/asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3", size = 3496243, upload-time = "2024-10-20T00:29:44.922Z" }, - { url = "https://files.pythonhosted.org/packages/f4/40/0ae9d061d278b10713ea9021ef6b703ec44698fe32178715a501ac696c6b/asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737", size = 3575059, upload-time = "2024-10-20T00:29:46.891Z" }, - { url = "https://files.pythonhosted.org/packages/c3/75/d6b895a35a2c6506952247640178e5f768eeb28b2e20299b6a6f1d743ba0/asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a", size = 3473596, upload-time = "2024-10-20T00:29:49.201Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e7/3693392d3e168ab0aebb2d361431375bd22ffc7b4a586a0fc060d519fae7/asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af", size = 3641632, upload-time = "2024-10-20T00:29:50.768Z" }, - { url = "https://files.pythonhosted.org/packages/32/ea/15670cea95745bba3f0352341db55f506a820b21c619ee66b7d12ea7867d/asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e", size = 560186, upload-time = "2024-10-20T00:29:52.394Z" }, - { url = "https://files.pythonhosted.org/packages/7e/6b/fe1fad5cee79ca5f5c27aed7bd95baee529c1bf8a387435c8ba4fe53d5c1/asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305", size = 621064, upload-time = "2024-10-20T00:29:53.757Z" }, -] - -[[package]] -name = "attrs" -version = "25.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/bb/73a1f1c64ee527877f64122422dafe5b87a846ccf4ac933fe21bcbb8fee8/authlib-1.6.4.tar.gz", hash = "sha256:104b0442a43061dc8bc23b133d1d06a2b0a9c2e3e33f34c4338929e816287649", size = 164046, upload-time = "2025-09-17T09:59:23.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/aa/91355b5f539caf1b94f0e66ff1e4ee39373b757fce08204981f7829ede51/authlib-1.6.4-py2.py3-none-any.whl", hash = "sha256:39313d2a2caac3ecf6d8f95fbebdfd30ae6ea6ae6a6db794d976405fdd9aa796", size = 243076, upload-time = "2025-09-17T09:59:22.259Z" }, -] - -[[package]] -name = "azure-core" -version = "1.35.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, - { name = "six" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/6b/2653adc0f33adba8f11b1903701e6b1c10d34ce5d8e25dfa13a422f832b0/azure_core-1.35.1.tar.gz", hash = "sha256:435d05d6df0fff2f73fb3c15493bb4721ede14203f1ff1382aa6b6b2bdd7e562", size = 345290, upload-time = "2025-09-11T22:58:04.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/52/805980aa1ba18282077c484dba634ef0ede1e84eec8be9c92b2e162d0ed6/azure_core-1.35.1-py3-none-any.whl", hash = "sha256:12da0c9e08e48e198f9158b56ddbe33b421477e1dc98c2e1c8f9e254d92c468b", size = 211800, upload-time = "2025-09-11T22:58:06.281Z" }, -] - -[[package]] -name = "azure-datalake-store" -version = "0.0.53" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, - { name = "msal" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/ff/61369d06422b5ac48067215ff404841342651b14a89b46c8d8e1507c8f17/azure-datalake-store-0.0.53.tar.gz", hash = "sha256:05b6de62ee3f2a0a6e6941e6933b792b800c3e7f6ffce2fc324bc19875757393", size = 71430, upload-time = "2023-05-10T21:17:05.665Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/2a/75f56b14f115189155cf12e46b366ad1fe3357af5a1a7c09f7446662d617/azure_datalake_store-0.0.53-py2.py3-none-any.whl", hash = "sha256:a30c902a6e360aa47d7f69f086b426729784e71c536f330b691647a51dc42b2b", size = 55308, upload-time = "2023-05-10T21:17:02.629Z" }, -] - -[[package]] -name = "azure-identity" -version = "1.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "azure-core" }, - { name = "cryptography" }, - { name = "msal" }, - { name = "msal-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4e/9e/4c9682a286c3c89e437579bd9f64f311020e5125c1321fd3a653166b5716/azure_identity-1.25.0.tar.gz", hash = "sha256:4177df34d684cddc026e6cf684e1abb57767aa9d84e7f2129b080ec45eee7733", size = 278507, upload-time = "2025-09-12T01:30:04.418Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/54/81683b6756676a22e037b209695b08008258e603f7e47c56834029c5922a/azure_identity-1.25.0-py3-none-any.whl", hash = "sha256:becaec086bbdf8d1a6aa4fb080c2772a0f824a97d50c29637ec8cc4933f1e82d", size = 190861, upload-time = "2025-09-12T01:30:06.474Z" }, -] - -[[package]] -name = "azure-storage-blob" -version = "12.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "azure-core" }, - { name = "cryptography" }, - { name = "isodate" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/95/3e3414491ce45025a1cde107b6ae72bf72049e6021597c201cd6a3029b9a/azure_storage_blob-12.26.0.tar.gz", hash = "sha256:5dd7d7824224f7de00bfeb032753601c982655173061e242f13be6e26d78d71f", size = 583332, upload-time = "2025-07-16T21:34:07.644Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/64/63dbfdd83b31200ac58820a7951ddfdeed1fbee9285b0f3eae12d1357155/azure_storage_blob-12.26.0-py3-none-any.whl", hash = "sha256:8c5631b8b22b4f53ec5fff2f3bededf34cfef111e2af613ad42c9e6de00a77fe", size = 412907, upload-time = "2025-07-16T21:34:09.367Z" }, -] - -[[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 = "banks" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecated" }, - { name = "griffe" }, - { name = "jinja2" }, - { name = "platformdirs" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/f8/25ef24814f77f3fd7f0fd3bd1ef3749e38a9dbd23502fbb53034de49900c/banks-2.2.0.tar.gz", hash = "sha256:d1446280ce6e00301e3e952dd754fd8cee23ff277d29ed160994a84d0d7ffe62", size = 179052, upload-time = "2025-07-18T16:28:26.892Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/d6/f9168956276934162ec8d48232f9920f2985ee45aa7602e3c6b4bc203613/banks-2.2.0-py3-none-any.whl", hash = "sha256:963cd5c85a587b122abde4f4064078def35c50c688c1b9d36f43c92503854e7d", size = 29244, upload-time = "2025-07-18T16:28:27.835Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, -] - -[[package]] -name = "blinker" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, -] - -[[package]] -name = "boto3" -version = "1.34.131" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "botocore" }, - { name = "jmespath" }, - { name = "s3transfer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/d9/35978a20f6f9a585ff83afb384faf71526a1b25c4131755b1cdb6687b1d9/boto3-1.34.131.tar.gz", hash = "sha256:dab8f72a6c4e62b4fd70da09e08a6b2a65ea2115b27dd63737142005776ef216", size = 108719, upload-time = "2024-06-20T19:34:56.629Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/ce/f5e3fdab6012f5fa4a8f5e97e86cc42549729382a98faffbc1785f85e89f/boto3-1.34.131-py3-none-any.whl", hash = "sha256:05e388cb937e82be70bfd7eb0c84cf8011ff35cf582a593873ac21675268683b", size = 139172, upload-time = "2024-06-20T19:34:44.219Z" }, -] - -[[package]] -name = "botocore" -version = "1.34.131" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/40/74bda5977985383b8ed403dced9d76ad5e1146db7b6c32089726b3130c8b/botocore-1.34.131.tar.gz", hash = "sha256:502ddafe1d627fcf1e4c007c86454e5dd011dba7c58bd8e8a5368a79f3e387dc", size = 12544482, upload-time = "2024-06-20T19:34:04.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/1a/01785fad12a9b1dbeffebd97cd226ea5923114057c64a610dd4eb8a28c7b/botocore-1.34.131-py3-none-any.whl", hash = "sha256:13b011d7b206ce00727dcee26548fa3b550db9046d5a0e90ac25a6e6c8fde6ef", size = 12332729, upload-time = "2024-06-20T19:33:51.589Z" }, -] - -[[package]] -name = "cachetools" -version = "6.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/61/e4fad8155db4a04bfb4734c7c8ff0882f078f24294d42798b3568eb63bff/cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32", size = 30988, upload-time = "2025-08-25T18:57:30.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/56/3124f61d37a7a4e7cc96afc5492c78ba0cb551151e530b54669ddd1436ef/cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6", size = 11276, upload-time = "2025-08-25T18:57:29.684Z" }, -] - -[[package]] -name = "certifi" -version = "2025.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, -] - -[[package]] -name = "cffi" -version = "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" }, -] - -[[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.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, -] - -[[package]] -name = "click" -version = "8.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, -] - -[[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.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/62/e3664e6ffd7743e1694b244dde70b43a394f6f7fbcacf7014a8ff5197c73/cryptography-46.0.1.tar.gz", hash = "sha256:ed570874e88f213437f5cf758f9ef26cbfc3f336d889b1e592ee11283bb8d1c7", size = 749198, upload-time = "2025-09-17T00:10:35.797Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/8c/44ee01267ec01e26e43ebfdae3f120ec2312aa72fa4c0507ebe41a26739f/cryptography-46.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:1cd6d50c1a8b79af1a6f703709d8973845f677c8e97b1268f5ff323d38ce8475", size = 7285044, upload-time = "2025-09-17T00:08:36.807Z" }, - { url = "https://files.pythonhosted.org/packages/22/59/9ae689a25047e0601adfcb159ec4f83c0b4149fdb5c3030cc94cd218141d/cryptography-46.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ff483716be32690c14636e54a1f6e2e1b7bf8e22ca50b989f88fa1b2d287080", size = 4308182, upload-time = "2025-09-17T00:08:39.388Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/ca6cc9df7118f2fcd142c76b1da0f14340d77518c05b1ebfbbabca6b9e7d/cryptography-46.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9873bf7c1f2a6330bdfe8621e7ce64b725784f9f0c3a6a55c3047af5849f920e", size = 4572393, upload-time = "2025-09-17T00:08:41.663Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a3/0f5296f63815d8e985922b05c31f77ce44787b3127a67c0b7f70f115c45f/cryptography-46.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0dfb7c88d4462a0cfdd0d87a3c245a7bc3feb59de101f6ff88194f740f72eda6", size = 4308400, upload-time = "2025-09-17T00:08:43.559Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8c/74fcda3e4e01be1d32775d5b4dd841acaac3c1b8fa4d0774c7ac8d52463d/cryptography-46.0.1-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e22801b61613ebdebf7deb18b507919e107547a1d39a3b57f5f855032dd7cfb8", size = 4015786, upload-time = "2025-09-17T00:08:45.758Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b8/85d23287baeef273b0834481a3dd55bbed3a53587e3b8d9f0898235b8f91/cryptography-46.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:757af4f6341ce7a1e47c326ca2a81f41d236070217e5fbbad61bbfe299d55d28", size = 4982606, upload-time = "2025-09-17T00:08:47.602Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d3/de61ad5b52433b389afca0bc70f02a7a1f074651221f599ce368da0fe437/cryptography-46.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f7a24ea78de345cfa7f6a8d3bde8b242c7fac27f2bd78fa23474ca38dfaeeab9", size = 4604234, upload-time = "2025-09-17T00:08:49.879Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1f/dbd4d6570d84748439237a7478d124ee0134bf166ad129267b7ed8ea6d22/cryptography-46.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e8776dac9e660c22241b6587fae51a67b4b0147daa4d176b172c3ff768ad736", size = 4307669, upload-time = "2025-09-17T00:08:52.321Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fd/ca0a14ce7f0bfe92fa727aacaf2217eb25eb7e4ed513b14d8e03b26e63ed/cryptography-46.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9f40642a140c0c8649987027867242b801486865277cbabc8c6059ddef16dc8b", size = 4947579, upload-time = "2025-09-17T00:08:54.697Z" }, - { url = "https://files.pythonhosted.org/packages/89/6b/09c30543bb93401f6f88fce556b3bdbb21e55ae14912c04b7bf355f5f96c/cryptography-46.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:449ef2b321bec7d97ef2c944173275ebdab78f3abdd005400cc409e27cd159ab", size = 4603669, upload-time = "2025-09-17T00:08:57.16Z" }, - { url = "https://files.pythonhosted.org/packages/23/9a/38cb01cb09ce0adceda9fc627c9cf98eb890fc8d50cacbe79b011df20f8a/cryptography-46.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2dd339ba3345b908fa3141ddba4025568fa6fd398eabce3ef72a29ac2d73ad75", size = 4435828, upload-time = "2025-09-17T00:08:59.606Z" }, - { url = "https://files.pythonhosted.org/packages/0f/53/435b5c36a78d06ae0bef96d666209b0ecd8f8181bfe4dda46536705df59e/cryptography-46.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7411c910fb2a412053cf33cfad0153ee20d27e256c6c3f14d7d7d1d9fec59fd5", size = 4709553, upload-time = "2025-09-17T00:09:01.832Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c4/0da6e55595d9b9cd3b6eb5dc22f3a07ded7f116a3ea72629cab595abb804/cryptography-46.0.1-cp311-abi3-win32.whl", hash = "sha256:cbb8e769d4cac884bb28e3ff620ef1001b75588a5c83c9c9f1fdc9afbe7f29b0", size = 3058327, upload-time = "2025-09-17T00:09:03.726Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/cd29a35e0d6e78a0ee61793564c8cff0929c38391cb0de27627bdc7525aa/cryptography-46.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:92e8cfe8bd7dd86eac0a677499894862cd5cc2fd74de917daa881d00871ac8e7", size = 3523893, upload-time = "2025-09-17T00:09:06.272Z" }, - { url = "https://files.pythonhosted.org/packages/f2/dd/eea390f3e78432bc3d2f53952375f8b37cb4d37783e626faa6a51e751719/cryptography-46.0.1-cp311-abi3-win_arm64.whl", hash = "sha256:db5597a4c7353b2e5fb05a8e6cb74b56a4658a2b7bf3cb6b1821ae7e7fd6eaa0", size = 2932145, upload-time = "2025-09-17T00:09:08.568Z" }, - { url = "https://files.pythonhosted.org/packages/98/e5/fbd632385542a3311915976f88e0dfcf09e62a3fc0aff86fb6762162a24d/cryptography-46.0.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d84c40bdb8674c29fa192373498b6cb1e84f882889d21a471b45d1f868d8d44b", size = 7255677, upload-time = "2025-09-17T00:09:42.407Z" }, - { url = "https://files.pythonhosted.org/packages/56/3e/13ce6eab9ad6eba1b15a7bd476f005a4c1b3f299f4c2f32b22408b0edccf/cryptography-46.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ed64e5083fa806709e74fc5ea067dfef9090e5b7a2320a49be3c9df3583a2d8", size = 4301110, upload-time = "2025-09-17T00:09:45.614Z" }, - { url = "https://files.pythonhosted.org/packages/a2/67/65dc233c1ddd688073cf7b136b06ff4b84bf517ba5529607c9d79720fc67/cryptography-46.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:341fb7a26bc9d6093c1b124b9f13acc283d2d51da440b98b55ab3f79f2522ead", size = 4562369, upload-time = "2025-09-17T00:09:47.601Z" }, - { url = "https://files.pythonhosted.org/packages/17/db/d64ae4c6f4e98c3dac5bf35dd4d103f4c7c345703e43560113e5e8e31b2b/cryptography-46.0.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6ef1488967e729948d424d09c94753d0167ce59afba8d0f6c07a22b629c557b2", size = 4302126, upload-time = "2025-09-17T00:09:49.335Z" }, - { url = "https://files.pythonhosted.org/packages/3d/19/5f1eea17d4805ebdc2e685b7b02800c4f63f3dd46cfa8d4c18373fea46c8/cryptography-46.0.1-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7823bc7cdf0b747ecfb096d004cc41573c2f5c7e3a29861603a2871b43d3ef32", size = 4009431, upload-time = "2025-09-17T00:09:51.239Z" }, - { url = "https://files.pythonhosted.org/packages/81/b5/229ba6088fe7abccbfe4c5edb96c7a5ad547fac5fdd0d40aa6ea540b2985/cryptography-46.0.1-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:f736ab8036796f5a119ff8211deda416f8c15ce03776db704a7a4e17381cb2ef", size = 4980739, upload-time = "2025-09-17T00:09:54.181Z" }, - { url = "https://files.pythonhosted.org/packages/3a/9c/50aa38907b201e74bc43c572f9603fa82b58e831bd13c245613a23cff736/cryptography-46.0.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e46710a240a41d594953012213ea8ca398cd2448fbc5d0f1be8160b5511104a0", size = 4592289, upload-time = "2025-09-17T00:09:56.731Z" }, - { url = "https://files.pythonhosted.org/packages/5a/33/229858f8a5bb22f82468bb285e9f4c44a31978d5f5830bb4ea1cf8a4e454/cryptography-46.0.1-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:84ef1f145de5aee82ea2447224dc23f065ff4cc5791bb3b506615957a6ba8128", size = 4301815, upload-time = "2025-09-17T00:09:58.548Z" }, - { url = "https://files.pythonhosted.org/packages/52/cb/b76b2c87fbd6ed4a231884bea3ce073406ba8e2dae9defad910d33cbf408/cryptography-46.0.1-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9394c7d5a7565ac5f7d9ba38b2617448eba384d7b107b262d63890079fad77ca", size = 4943251, upload-time = "2025-09-17T00:10:00.475Z" }, - { url = "https://files.pythonhosted.org/packages/94/0f/f66125ecf88e4cb5b8017ff43f3a87ede2d064cb54a1c5893f9da9d65093/cryptography-46.0.1-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ed957044e368ed295257ae3d212b95456bd9756df490e1ac4538857f67531fcc", size = 4591247, upload-time = "2025-09-17T00:10:02.874Z" }, - { url = "https://files.pythonhosted.org/packages/f6/22/9f3134ae436b63b463cfdf0ff506a0570da6873adb4bf8c19b8a5b4bac64/cryptography-46.0.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f7de12fa0eee6234de9a9ce0ffcfa6ce97361db7a50b09b65c63ac58e5f22fc7", size = 4428534, upload-time = "2025-09-17T00:10:04.994Z" }, - { url = "https://files.pythonhosted.org/packages/89/39/e6042bcb2638650b0005c752c38ea830cbfbcbb1830e4d64d530000aa8dc/cryptography-46.0.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7fab1187b6c6b2f11a326f33b036f7168f5b996aedd0c059f9738915e4e8f53a", size = 4699541, upload-time = "2025-09-17T00:10:06.925Z" }, - { url = "https://files.pythonhosted.org/packages/68/46/753d457492d15458c7b5a653fc9a84a1c9c7a83af6ebdc94c3fc373ca6e8/cryptography-46.0.1-cp38-abi3-win32.whl", hash = "sha256:45f790934ac1018adeba46a0f7289b2b8fe76ba774a88c7f1922213a56c98bc1", size = 3043779, upload-time = "2025-09-17T00:10:08.951Z" }, - { url = "https://files.pythonhosted.org/packages/2f/50/b6f3b540c2f6ee712feeb5fa780bb11fad76634e71334718568e7695cb55/cryptography-46.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:7176a5ab56fac98d706921f6416a05e5aff7df0e4b91516f450f8627cda22af3", size = 3517226, upload-time = "2025-09-17T00:10:10.769Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e8/77d17d00981cdd27cc493e81e1749a0b8bbfb843780dbd841e30d7f50743/cryptography-46.0.1-cp38-abi3-win_arm64.whl", hash = "sha256:efc9e51c3e595267ff84adf56e9b357db89ab2279d7e375ffcaf8f678606f3d9", size = 2923149, upload-time = "2025-09-17T00:10:13.236Z" }, -] - -[[package]] -name = "dataclasses-json" -version = "0.6.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "marshmallow" }, - { name = "typing-inspect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, -] - -[[package]] -name = "dataproperty" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mbstrdecoder" }, - { name = "typepy", extra = ["datetime"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/81/8c8b64ae873cb9014815214c07b63b12e3b18835780fb342223cfe3fe7d8/dataproperty-1.1.0.tar.gz", hash = "sha256:b038437a4097d1a1c497695c3586ea34bea67fdd35372b9a50f30bf044d77d04", size = 42574, upload-time = "2024-12-31T14:37:26.033Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/c2/e12e95e289e6081a40454199ab213139ef16a528c7c86432de545b05a23a/DataProperty-1.1.0-py3-none-any.whl", hash = "sha256:c61fcb2e2deca35e6d1eb1f251a7f22f0dcde63e80e61f0cc18c19f42abfd25b", size = 27581, upload-time = "2024-12-31T14:37:22.657Z" }, -] - -[[package]] -name = "debugpy" -version = "1.8.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129, upload-time = "2025-09-17T16:33:20.633Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/2b/9d8e65beb2751876c82e1aceb32f328c43ec872711fa80257c7674f45650/debugpy-1.8.17-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:f14467edef672195c6f6b8e27ce5005313cb5d03c9239059bc7182b60c176e2d", size = 2549522, upload-time = "2025-09-17T16:33:38.466Z" }, - { url = "https://files.pythonhosted.org/packages/b4/78/eb0d77f02971c05fca0eb7465b18058ba84bd957062f5eec82f941ac792a/debugpy-1.8.17-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:24693179ef9dfa20dca8605905a42b392be56d410c333af82f1c5dff807a64cc", size = 4309417, upload-time = "2025-09-17T16:33:41.299Z" }, - { url = "https://files.pythonhosted.org/packages/37/42/c40f1d8cc1fed1e75ea54298a382395b8b937d923fcf41ab0797a554f555/debugpy-1.8.17-cp312-cp312-win32.whl", hash = "sha256:6a4e9dacf2cbb60d2514ff7b04b4534b0139facbf2abdffe0639ddb6088e59cf", size = 5277130, upload-time = "2025-09-17T16:33:43.554Z" }, - { url = "https://files.pythonhosted.org/packages/72/22/84263b205baad32b81b36eac076de0cdbe09fe2d0637f5b32243dc7c925b/debugpy-1.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:e8f8f61c518952fb15f74a302e068b48d9c4691768ade433e4adeea961993464", size = 5319053, upload-time = "2025-09-17T16:33:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210, upload-time = "2025-09-17T16:34:25.835Z" }, -] - -[[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 = "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 = "deprecated" -version = "1.2.18" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, -] - -[[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 = "dirtyjson" -version = "1.0.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/04/d24f6e645ad82ba0ef092fa17d9ef7a21953781663648a01c9371d9e8e98/dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd", size = 30782, upload-time = "2022-11-28T23:32:33.319Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/69/1bcf70f81de1b4a9f21b3a62ec0c83bdff991c88d6cc2267d02408457e88/dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53", size = 25197, upload-time = "2022-11-28T23:32:31.219Z" }, -] - -[[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 = "fastuuid" -version = "0.13.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/80/3c16a1edad2e6cd82fbd15ac998cc1b881f478bf1f80ca717d941c441874/fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1", size = 18255, upload-time = "2025-09-26T09:05:38.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/36/434f137c5970cac19e57834e1f7680e85301619d49891618c00666700c61/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a", size = 494638, upload-time = "2025-09-26T09:14:58.695Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3c/083de2ac007b2b305523b9c006dba5051e5afd87a626ef1a39f76e2c6b82/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7", size = 253138, upload-time = "2025-09-26T09:13:33.283Z" }, - { url = "https://files.pythonhosted.org/packages/73/5e/630cffa1c8775db526e39e9e4c5c7db0c27be0786bb21ba82c912ae19f63/fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202", size = 244521, upload-time = "2025-09-26T09:14:40.682Z" }, - { url = "https://files.pythonhosted.org/packages/4d/51/55d78705f4fbdadf88fb40f382f508d6c7a4941ceddd7825fafebb4cc778/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e", size = 271557, upload-time = "2025-09-26T09:15:09.75Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2b/1b89e90a8635e5587ccdbbeb169c590672ce7637880f2c047482a0359950/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854", size = 272334, upload-time = "2025-09-26T09:07:48.865Z" }, - { url = "https://files.pythonhosted.org/packages/0c/06/4c8207894eeb30414999e5c3f66ac039bc4003437eb4060d8a1bceb4cc6f/fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86", size = 290594, upload-time = "2025-09-26T09:12:54.124Z" }, - { url = "https://files.pythonhosted.org/packages/50/69/96d221931a31d77a47cc2487bdfacfb3091edfc2e7a04b1795df1aec05df/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952", size = 452835, upload-time = "2025-09-26T09:14:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/25/ef/bf045f0a47dcec96247497ef3f7a31d86ebc074330e2dccc34b8dbc0468a/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9", size = 468225, upload-time = "2025-09-26T09:13:38.585Z" }, - { url = "https://files.pythonhosted.org/packages/30/46/4817ab5a3778927155a4bde92540d4c4fa996161ec8b8e080c8928b0984e/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3", size = 444907, upload-time = "2025-09-26T09:14:30.163Z" }, - { url = "https://files.pythonhosted.org/packages/80/27/ab284117ce4dc9b356a7196bdbf220510285f201d27f1f078592cdc8187b/fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85", size = 145415, upload-time = "2025-09-26T09:08:59.494Z" }, - { url = "https://files.pythonhosted.org/packages/f4/0c/f970a4222773b248931819f8940800b760283216ca3dda173ed027e94bdd/fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a", size = 150840, upload-time = "2025-09-26T09:13:46.115Z" }, -] - -[[package]] -name = "filelock" -version = "3.19.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, -] - -[[package]] -name = "filetype" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, -] - -[[package]] -name = "flask" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blinker" }, - { name = "click" }, - { name = "itsdangerous" }, - { name = "jinja2" }, - { name = "markupsafe" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160, upload-time = "2025-08-19T21:03:21.205Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" }, -] - -[[package]] -name = "flask-wtf" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flask" }, - { name = "itsdangerous" }, - { name = "wtforms" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/80/9b/f1cd6e41bbf874f3436368f2c7ee3216c1e82d666ff90d1d800e20eb1317/flask_wtf-1.2.2.tar.gz", hash = "sha256:79d2ee1e436cf570bccb7d916533fa18757a2f18c290accffab1b9a0b684666b", size = 42641, upload-time = "2024-10-24T07:18:58.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/19/354449145fbebb65e7c621235b6ad69bebcfaec2142481f044d0ddc5b5c5/flask_wtf-1.2.2-py3-none-any.whl", hash = "sha256:e93160c5c5b6b571cf99300b6e01b72f9a101027cab1579901f8b10c5daf0b70", size = 12779, upload-time = "2024-10-24T07:18:56.976Z" }, -] - -[[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/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/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 = "2024.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a0/52/f16a068ebadae42526484c31f4398e62962504e5724a8ba5dc3409483df2/fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493", size = 286853, upload-time = "2024-10-21T01:21:16.969Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b2/454d6e7f0158951d8a78c2e1eb4f69ae81beb8dca5fee9809c6c99e9d0d0/fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871", size = 179641, upload-time = "2024-10-21T01:21:14.793Z" }, -] - -[[package]] -name = "gcsfs" -version = "2024.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "decorator" }, - { name = "fsspec" }, - { name = "google-auth" }, - { name = "google-auth-oauthlib" }, - { name = "google-cloud-storage" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/1e/1d8c4593d9e2eb04918fec43253ab152823d67ad51ad9e3ab6b3a78c431a/gcsfs-2024.10.0.tar.gz", hash = "sha256:5df54cfe568e8fdeea5aafa7fed695cdc69a9a674e991ca8c1ce634f5df1d314", size = 79588, upload-time = "2024-10-21T13:43:26.163Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/96/d60e835fb7d10166c77aef0c1fa30e634153c03a0f486786977b95f88fde/gcsfs-2024.10.0-py2.py3-none-any.whl", hash = "sha256:bb2d23547e61203ea2dda5fa6c4b91a0c34b74ebe8bb6ab1926f6c33381bceb2", size = 34953, upload-time = "2024-10-21T13:43:24.951Z" }, -] - -[[package]] -name = "google-api-core" -version = "2.25.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "googleapis-common-protos" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" }, -] - -[[package]] -name = "google-auth" -version = "2.41.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "pyasn1-modules" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/c5/87742f5b5f055514c67f970f7174a876fccff2289a69d460b0614cc7ccfb/google_auth-2.41.0.tar.gz", hash = "sha256:c9d7b534ea4a5d9813c552846797fafb080312263cd4994d6622dd50992ae101", size = 292282, upload-time = "2025-09-29T21:36:35.791Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/ff/a1c426fc9bea7268230bf92340da7d112fae18cf946cafe13ab17d14e6ee/google_auth-2.41.0-py2.py3-none-any.whl", hash = "sha256:d8bed9b53ab63b7b0374656b8e1bef051f95bb14ecc0cf21ba49de7911d62e09", size = 221168, upload-time = "2025-09-29T21:36:33.925Z" }, -] - -[[package]] -name = "google-auth-oauthlib" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "requests-oauthlib" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fb/87/e10bf24f7bcffc1421b84d6f9c3377c30ec305d082cd737ddaa6d8f77f7c/google_auth_oauthlib-1.2.2.tar.gz", hash = "sha256:11046fb8d3348b296302dd939ace8af0a724042e8029c1b872d87fabc9f41684", size = 20955, upload-time = "2025-04-22T16:40:29.172Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/84/40ee070be95771acd2f4418981edb834979424565c3eec3cd88b6aa09d24/google_auth_oauthlib-1.2.2-py3-none-any.whl", hash = "sha256:fd619506f4b3908b5df17b65f39ca8d66ea56986e5472eb5978fd8f3786f00a2", size = 19072, upload-time = "2025-04-22T16:40:28.174Z" }, -] - -[[package]] -name = "google-cloud-core" -version = "2.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, -] - -[[package]] -name = "google-cloud-storage" -version = "2.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-crc32c" }, - { name = "google-resumable-media" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/76/4d965702e96bb67976e755bed9828fa50306dca003dbee08b67f41dd265e/google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2", size = 5535488, upload-time = "2024-12-05T01:35:06.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/94/6db383d8ee1adf45dc6c73477152b82731fa4c4a46d9c1932cc8757e0fd4/google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba", size = 131787, upload-time = "2024-12-05T01:35:04.736Z" }, -] - -[[package]] -name = "google-crc32c" -version = "1.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload-time = "2025-03-26T14:34:31.655Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload-time = "2025-03-26T15:01:54.634Z" }, - { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload-time = "2025-03-26T14:41:32.168Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload-time = "2025-03-26T14:41:33.264Z" }, - { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload-time = "2025-03-26T14:29:10.94Z" }, -] - -[[package]] -name = "google-resumable-media" -version = "2.7.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-crc32c" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099, upload-time = "2024-08-07T22:20:38.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251, upload-time = "2024-08-07T22:20:36.409Z" }, -] - -[[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 = "greenlet" -version = "3.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, - { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, - { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, - { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, - { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" }, - { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, -] - -[[package]] -name = "griffe" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, -] - -[[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" }, -] - -[[package]] -name = "grpcio-tools" -version = "1.62.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "protobuf" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/fa/b69bd8040eafc09b88bb0ec0fea59e8aacd1a801e688af087cead213b0d0/grpcio-tools-1.62.3.tar.gz", hash = "sha256:7c7136015c3d62c3eef493efabaf9e3380e3e66d24ee8e94c01cb71377f57833", size = 4538520, upload-time = "2024-08-06T00:37:11.035Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/a5/d6887eba415ce318ae5005e8dfac3fa74892400b54b6d37b79e8b4f14f5e/grpcio_tools-1.62.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d102b9b21c4e1e40af9a2ab3c6d41afba6bd29c0aa50ca013bf85c99cdc44ac5", size = 5147690, upload-time = "2024-08-06T00:31:16.436Z" }, - { url = "https://files.pythonhosted.org/packages/8a/7c/3cde447a045e83ceb4b570af8afe67ffc86896a2fe7f59594dc8e5d0a645/grpcio_tools-1.62.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:0a52cc9444df978438b8d2332c0ca99000521895229934a59f94f37ed896b133", size = 2720538, upload-time = "2024-08-06T00:31:18.905Z" }, - { url = "https://files.pythonhosted.org/packages/88/07/f83f2750d44ac4f06c07c37395b9c1383ef5c994745f73c6bfaf767f0944/grpcio_tools-1.62.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141d028bf5762d4a97f981c501da873589df3f7e02f4c1260e1921e565b376fa", size = 3071571, upload-time = "2024-08-06T00:31:21.684Z" }, - { url = "https://files.pythonhosted.org/packages/37/74/40175897deb61e54aca716bc2e8919155b48f33aafec8043dda9592d8768/grpcio_tools-1.62.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47a5c093ab256dec5714a7a345f8cc89315cb57c298b276fa244f37a0ba507f0", size = 2806207, upload-time = "2024-08-06T00:31:24.208Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/d8de915105a217cbcb9084d684abdc032030dcd887277f2ef167372287fe/grpcio_tools-1.62.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f6831fdec2b853c9daa3358535c55eed3694325889aa714070528cf8f92d7d6d", size = 3685815, upload-time = "2024-08-06T00:31:26.917Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d9/4360a6c12be3d7521b0b8c39e5d3801d622fbb81cc2721dbd3eee31e28c8/grpcio_tools-1.62.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e02d7c1a02e3814c94ba0cfe43d93e872c758bd8fd5c2797f894d0c49b4a1dfc", size = 3298378, upload-time = "2024-08-06T00:31:30.401Z" }, - { url = "https://files.pythonhosted.org/packages/29/3b/7cdf4a9e5a3e0a35a528b48b111355cd14da601413a4f887aa99b6da468f/grpcio_tools-1.62.3-cp312-cp312-win32.whl", hash = "sha256:b881fd9505a84457e9f7e99362eeedd86497b659030cf57c6f0070df6d9c2b9b", size = 910416, upload-time = "2024-08-06T00:31:33.118Z" }, - { url = "https://files.pythonhosted.org/packages/6c/66/dd3ec249e44c1cc15e902e783747819ed41ead1336fcba72bf841f72c6e9/grpcio_tools-1.62.3-cp312-cp312-win_amd64.whl", hash = "sha256:11c625eebefd1fd40a228fc8bae385e448c7e32a6ae134e43cf13bbc23f902b7", size = 1052856, upload-time = "2024-08-06T00:31:36.519Z" }, -] - -[[package]] -name = "gunicorn" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, -] - -[[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 = "hf-xet" -version = "1.1.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97", size = 487910, upload-time = "2025-09-12T20:10:27.12Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d", size = 2761466, upload-time = "2025-09-12T20:10:22.836Z" }, - { url = "https://files.pythonhosted.org/packages/31/f9/6215f948ac8f17566ee27af6430ea72045e0418ce757260248b483f4183b/hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b", size = 2623807, upload-time = "2025-09-12T20:10:21.118Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/86397573efefff941e100367bbda0b21496ffcdb34db7ab51912994c32a2/hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435", size = 3186960, upload-time = "2025-09-12T20:10:19.336Z" }, - { url = "https://files.pythonhosted.org/packages/01/a7/0b2e242b918cc30e1f91980f3c4b026ff2eedaf1e2ad96933bca164b2869/hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c", size = 3087167, upload-time = "2025-09-12T20:10:17.255Z" }, - { url = "https://files.pythonhosted.org/packages/4a/25/3e32ab61cc7145b11eee9d745988e2f0f4fafda81b25980eebf97d8cff15/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06", size = 3248612, upload-time = "2025-09-12T20:10:24.093Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3d/ab7109e607ed321afaa690f557a9ada6d6d164ec852fd6bf9979665dc3d6/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f", size = 3353360, upload-time = "2025-09-12T20:10:25.563Z" }, - { url = "https://files.pythonhosted.org/packages/ee/0e/471f0a21db36e71a2f1752767ad77e92d8cde24e974e03d662931b1305ec/hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045", size = 2804691, upload-time = "2025-09-12T20:10:28.433Z" }, -] - -[[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 = "huggingface-hub" -version = "0.35.3" -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/10/7e/a0a97de7c73671863ca6b3f61fa12518caf35db37825e43d63a70956738c/huggingface_hub-0.35.3.tar.gz", hash = "sha256:350932eaa5cc6a4747efae85126ee220e4ef1b54e29d31c3b45c5612ddf0b32a", size = 461798, upload-time = "2025-09-29T14:29:58.625Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/a0/651f93d154cb72323358bf2bbae3e642bdb5d2f1bfc874d096f7cb159fa0/huggingface_hub-0.35.3-py3-none-any.whl", hash = "sha256:0e3a01829c19d86d03793e4577816fe3bdfc1602ac62c7fb220d593d351224ba", size = 564262, upload-time = "2025-09-29T14:29:55.813Z" }, -] - -[[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.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 = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "isodate" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, -] - -[[package]] -name = "itsdangerous" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, -] - -[[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.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/a3bb4cc13aced219dd18191ea66e874266bd8aa7b96744e495e1c733aa2d/jiter-0.11.0.tar.gz", hash = "sha256:1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4", size = 167094, upload-time = "2025-09-15T09:20:38.212Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/b5/3009b112b8f673e568ef79af9863d8309a15f0a8cdcc06ed6092051f377e/jiter-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb7b377688cc3850bbe5c192a6bd493562a0bc50cbc8b047316428fbae00ada", size = 305510, upload-time = "2025-09-15T09:19:25.893Z" }, - { url = "https://files.pythonhosted.org/packages/fe/82/15514244e03b9e71e086bbe2a6de3e4616b48f07d5f834200c873956fb8c/jiter-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b7cbe3f25bd0d8abb468ba4302a5d45617ee61b2a7a638f63fee1dc086be99", size = 316521, upload-time = "2025-09-15T09:19:27.525Z" }, - { url = "https://files.pythonhosted.org/packages/92/94/7a2e905f40ad2d6d660e00b68d818f9e29fb87ffe82774f06191e93cbe4a/jiter-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0a7f0ec81d5b7588c5cade1eb1925b91436ae6726dc2df2348524aeabad5de6", size = 338214, upload-time = "2025-09-15T09:19:28.727Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/5791ed5bdc76f12110158d3316a7a3ec0b1413d018b41c5ed399549d3ad5/jiter-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07630bb46ea2a6b9c6ed986c6e17e35b26148cce2c535454b26ee3f0e8dcaba1", size = 361280, upload-time = "2025-09-15T09:19:30.013Z" }, - { url = "https://files.pythonhosted.org/packages/d4/7f/b7d82d77ff0d2cb06424141000176b53a9e6b16a1125525bb51ea4990c2e/jiter-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7764f27d28cd4a9cbc61704dfcd80c903ce3aad106a37902d3270cd6673d17f4", size = 487895, upload-time = "2025-09-15T09:19:31.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/44/10a1475d46f1fc1fd5cc2e82c58e7bca0ce5852208e0fa5df2f949353321/jiter-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4a6c4a737d486f77f842aeb22807edecb4a9417e6700c7b981e16d34ba7c72", size = 378421, upload-time = "2025-09-15T09:19:32.746Z" }, - { url = "https://files.pythonhosted.org/packages/9a/5f/0dc34563d8164d31d07bc09d141d3da08157a68dcd1f9b886fa4e917805b/jiter-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf408d2a0abd919b60de8c2e7bc5eeab72d4dafd18784152acc7c9adc3291591", size = 347932, upload-time = "2025-09-15T09:19:34.612Z" }, - { url = "https://files.pythonhosted.org/packages/f7/de/b68f32a4fcb7b4a682b37c73a0e5dae32180140cd1caf11aef6ad40ddbf2/jiter-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdef53eda7d18e799625023e1e250dbc18fbc275153039b873ec74d7e8883e09", size = 386959, upload-time = "2025-09-15T09:19:35.994Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/c08c92e713b6e28972a846a81ce374883dac2f78ec6f39a0dad9f2339c3a/jiter-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:53933a38ef7b551dd9c7f1064f9d7bb235bb3168d0fa5f14f0798d1b7ea0d9c5", size = 517187, upload-time = "2025-09-15T09:19:37.426Z" }, - { url = "https://files.pythonhosted.org/packages/89/b5/4a283bec43b15aad54fcae18d951f06a2ec3f78db5708d3b59a48e9c3fbd/jiter-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11840d2324c9ab5162fc1abba23bc922124fedcff0d7b7f85fffa291e2f69206", size = 509461, upload-time = "2025-09-15T09:19:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/34/a5/f8bad793010534ea73c985caaeef8cc22dfb1fedb15220ecdf15c623c07a/jiter-0.11.0-cp312-cp312-win32.whl", hash = "sha256:4f01a744d24a5f2bb4a11657a1b27b61dc038ae2e674621a74020406e08f749b", size = 206664, upload-time = "2025-09-15T09:19:40.096Z" }, - { url = "https://files.pythonhosted.org/packages/ed/42/5823ec2b1469395a160b4bf5f14326b4a098f3b6898fbd327366789fa5d3/jiter-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:29fff31190ab3a26de026da2f187814f4b9c6695361e20a9ac2123e4d4378a4c", size = 203520, upload-time = "2025-09-15T09:19:41.798Z" }, -] - -[[package]] -name = "jmespath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55", size = 331077, upload-time = "2025-08-27T12:15:46.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396, upload-time = "2025-08-27T12:15:45.188Z" }, -] - -[[package]] -name = "json-repair" -version = "0.42.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/1e/aea70e484e271e3d841773d5d185bedf488cf8f881088d47faae31f19116/json_repair-0.42.0.tar.gz", hash = "sha256:1a901f706c5b6b4325f0f79b53b0d998c5b327070e98b530da71cc5a3eda8616", size = 31349, upload-time = "2025-04-22T11:12:17.692Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c7/dd23f764de95771300a8a7ae17293c47ba0e48f826154229d18ecfe147cd/json_repair-0.42.0-py3-none-any.whl", hash = "sha256:7b6805162053dfe65722e961bc51b5eecec0582ec8a8e0fd218d33e8de757daf", size = 21612, upload-time = "2025-04-22T11:12:16.302Z" }, -] - -[[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-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 = "kombu" -version = "5.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "amqp" }, - { name = "packaging" }, - { name = "tzdata" }, - { name = "vine" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/d3/5ff936d8319ac86b9c409f1501b07c426e6ad41966fedace9ef1b966e23f/kombu-5.5.4.tar.gz", hash = "sha256:886600168275ebeada93b888e831352fe578168342f0d1d5833d88ba0d847363", size = 461992, upload-time = "2025-06-01T10:19:22.281Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/70/a07dcf4f62598c8ad579df241af55ced65bed76e42e45d3c368a6d82dbc1/kombu-5.5.4-py3-none-any.whl", hash = "sha256:a12ed0557c238897d8e518f1d1fdf84bd1516c5e305af2dacd85c2015115feb8", size = 210034, upload-time = "2025-06-01T10:19:20.436Z" }, -] - -[[package]] -name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } -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" }, -] - -[[package]] -name = "llama-cloud" -version = "0.1.35" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "httpx" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/72/816e6e900448e1b4a8137d90e65876b296c5264a23db6ae888bd3e6660ba/llama_cloud-0.1.35.tar.gz", hash = "sha256:200349d5d57424d7461f304cdb1355a58eea3e6ca1e6b0d75c66b2e937216983", size = 106403, upload-time = "2025-07-28T17:22:06.41Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/d2/8d18a021ab757cea231428404f21fe3186bf1ebaac3f57a73c379483fd3f/llama_cloud-0.1.35-py3-none-any.whl", hash = "sha256:b7abab4423118e6f638d2f326749e7a07c6426543bea6da99b623c715b22af71", size = 303280, upload-time = "2025-07-28T17:22:04.946Z" }, -] - -[[package]] -name = "llama-cloud-services" -version = "0.6.54" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "llama-cloud" }, - { name = "llama-index-core" }, - { name = "platformdirs" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tenacity" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/0c/8ca87d33bea0340a8ed791f36390112aeb29fd3eebfd64b6aef6204a03f0/llama_cloud_services-0.6.54.tar.gz", hash = "sha256:baf65d9bffb68f9dca98ac6e22908b6675b2038b021e657ead1ffc0e43cbd45d", size = 53468, upload-time = "2025-08-01T20:09:20.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/48/4e295e3f791b279885a2e584f71e75cbe4ac84e93bba3c36e2668f60a8ac/llama_cloud_services-0.6.54-py3-none-any.whl", hash = "sha256:07f595f7a0ba40c6a1a20543d63024ca7600fe65c4811d1951039977908997be", size = 63874, upload-time = "2025-08-01T20:09:20.076Z" }, -] - -[[package]] -name = "llama-index" -version = "0.14.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-cli" }, - { name = "llama-index-core" }, - { name = "llama-index-embeddings-openai" }, - { name = "llama-index-indices-managed-llama-cloud" }, - { name = "llama-index-llms-openai" }, - { name = "llama-index-readers-file" }, - { name = "llama-index-readers-llama-parse" }, - { name = "nltk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/26/e46c43ca97f849169d54b1c11e599ea51e650f4c27355c28e384cb0c7e94/llama_index-0.14.13.tar.gz", hash = "sha256:6392822f8ad0e747da2f0adbfcd170bc91fafdff4341cd50da809feae5ca828a", size = 8461, upload-time = "2026-01-21T20:44:49.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/32/5c13c0b0e7fdedaf4f98a137a64558ee87bbdbc14e16b394c103f6a5418e/llama_index-0.14.13-py3-none-any.whl", hash = "sha256:54b3c15d9327a05c981f332643e15a7adc52168d173302987aeb0f9dc7f0267c", size = 7459, upload-time = "2026-01-21T20:44:47.96Z" }, -] - -[[package]] -name = "llama-index-cli" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "llama-index-embeddings-openai" }, - { name = "llama-index-llms-openai" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/84/41e820efffbe327c38228d3b37fe42512a37e0c3ee4ff6bf97a394e9577a/llama_index_cli-0.5.3.tar.gz", hash = "sha256:ebaf39e785efbfa8d50d837f60cb0f95125c04bf73ed1f92092a2a5f506172f8", size = 24821, upload-time = "2025-09-29T18:03:10.798Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/81/b7b3778aa8662913760fbbee77578daf4407aeaa677ccbf0125c4cfa2e67/llama_index_cli-0.5.3-py3-none-any.whl", hash = "sha256:7deb1e953e582bd885443881ce8bd6ab2817b594fef00079dce9993c47d990f7", size = 28173, upload-time = "2025-09-29T18:03:10.024Z" }, -] - -[[package]] -name = "llama-index-core" -version = "0.14.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aiosqlite" }, - { name = "banks" }, - { name = "dataclasses-json" }, - { name = "deprecated" }, - { name = "dirtyjson" }, - { name = "filetype" }, - { name = "fsspec" }, - { name = "httpx" }, - { name = "llama-index-workflows" }, - { name = "nest-asyncio" }, - { name = "networkx" }, - { name = "nltk" }, - { name = "numpy" }, - { name = "pillow" }, - { name = "platformdirs" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "setuptools" }, - { name = "sqlalchemy", extra = ["asyncio"] }, - { name = "tenacity" }, - { name = "tiktoken" }, - { name = "tqdm" }, - { name = "typing-extensions" }, - { name = "typing-inspect" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/54/d6043a088e5e9c1d62300db7ad0ef417c6b9a92f7b4a5cade066aeafdaca/llama_index_core-0.14.13.tar.gz", hash = "sha256:c3b30d20ae0407e5d0a1d35bb3376a98e242661ebfc22da754b5a3da1f8108c0", size = 11589074, upload-time = "2026-01-21T20:44:16.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/59/9769f03f1cccadcc014b3b65c166de18999b51459a0f0a579d80f6c91d80/llama_index_core-0.14.13-py3-none-any.whl", hash = "sha256:392f0a5a09433e9dea786964ef5fe5ca2a2b10aee9f979a9507c19a14da2a20a", size = 11934761, upload-time = "2026-01-21T20:44:18.892Z" }, -] - -[[package]] -name = "llama-index-embeddings-openai" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "openai" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/10/36/90336d054a5061a3f5bc17ac2c18ef63d9d84c55c14d557de484e811ea4d/llama_index_embeddings_openai-0.5.1.tar.gz", hash = "sha256:1c89867a48b0d0daa3d2d44f5e76b394b2b2ef9935932daf921b9e77939ccda8", size = 7020, upload-time = "2025-09-08T20:17:44.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/4a/8ab11026cf8deff8f555aa73919be0bac48332683111e5fc4290f352dc50/llama_index_embeddings_openai-0.5.1-py3-none-any.whl", hash = "sha256:a2fcda3398bbd987b5ce3f02367caee8e84a56b930fdf43cc1d059aa9fd20ca5", size = 7011, upload-time = "2025-09-08T20:17:44.015Z" }, -] - -[[package]] -name = "llama-index-indices-managed-llama-cloud" -version = "0.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecated" }, - { name = "llama-cloud" }, - { name = "llama-index-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/4a/79044fcb3209583d1ffe0c2a7c19dddfb657a03faeb9fe0cf5a74027e646/llama_index_indices_managed_llama_cloud-0.9.4.tar.gz", hash = "sha256:b5e00752ab30564abf19c57595a2107f5697c3b03b085817b4fca84a38ebbd59", size = 15146, upload-time = "2025-09-08T20:29:58.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/6a/0e33245df06afc9766c46a1fe92687be8a09da5d0d0128bc08d84a9f5efa/llama_index_indices_managed_llama_cloud-0.9.4-py3-none-any.whl", hash = "sha256:535a08811046803ca6ab7f8e9d510e926aa5306608b02201ad3d9d21701383bc", size = 17005, upload-time = "2025-09-08T20:29:57.876Z" }, -] - -[[package]] -name = "llama-index-instrumentation" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecated" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/e5/a3628da5d716d6bbc2c0a8d39b629dff81b33d5625c5b934e1456370064f/llama_index_instrumentation-0.4.1.tar.gz", hash = "sha256:a79d0dd2baba34f05ff4354d63a99b212322635b8afa6cc96ed00a7e11ebfdc3", size = 45788, upload-time = "2025-09-15T03:53:00.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/7a/c414f4dc9a7dd90d050c387489436bab2d678a566b704ede2f5b62f82ad7/llama_index_instrumentation-0.4.1-py3-none-any.whl", hash = "sha256:0d3ac926d0db3d39c0ec34ee72da5322d61e06b87fe956407e4a1e7a2708b936", size = 15063, upload-time = "2025-09-15T03:52:59.098Z" }, -] - -[[package]] -name = "llama-index-llms-openai" -version = "0.6.17" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "openai" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/94/55e9855f565b30604fe9e154a7c3df1f4437ce62ed8926a8d6b5112cf73b/llama_index_llms_openai-0.6.17.tar.gz", hash = "sha256:63294c1d8d221d2009023b2c294c919f6141f4cdce9cd48bffe700d8be2a37e0", size = 25941, upload-time = "2026-02-03T11:03:26.355Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/85/1dee39a45b31528611cc6139777fc5631b9946fe085b37cd981062aa6d50/llama_index_llms_openai-0.6.17-py3-none-any.whl", hash = "sha256:d275f7e218611afc6bb157fe722f6e8590e8e20ac3ea1392cf560ac674c175af", size = 26940, upload-time = "2026-02-03T11:03:25.059Z" }, -] - -[[package]] -name = "llama-index-readers-file" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "defusedxml" }, - { name = "llama-index-core" }, - { name = "pandas" }, - { name = "pypdf" }, - { name = "striprtf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/d9/c67ad2b9cba8dacf1d4a55fe5432357b6eceaecfb096a0de5c1cbd959b98/llama_index_readers_file-0.5.4.tar.gz", hash = "sha256:5e766f32597622e66529464101914548ad683770a0a5d2bdc9ee84eb3a110332", size = 32565, upload-time = "2025-09-08T20:39:40.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/e3/76d72a7281b9c88d488908731c9034e1ee1a2cad5aa1dead76b051eca989/llama_index_readers_file-0.5.4-py3-none-any.whl", hash = "sha256:135be5ddda66c5b35883911918b2d99f67a2ab010d180af5630c872ea9509d45", size = 51827, upload-time = "2025-09-08T20:39:39.408Z" }, -] - -[[package]] -name = "llama-index-readers-llama-parse" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "llama-parse" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/77/5bfaab20e6ec8428dbf2352e18be550c957602723d69383908176b5686cd/llama_index_readers_llama_parse-0.5.1.tar.gz", hash = "sha256:2b78b73faa933e30e6c69df351e4e9f36dfe2ae142e2ab3969ddd2ac48930e37", size = 3858, upload-time = "2025-09-08T20:41:29.201Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/81/52410c7245dcbf1a54756a9ce3892cdd167ec0b884d696de1304ca3f452e/llama_index_readers_llama_parse-0.5.1-py3-none-any.whl", hash = "sha256:0d41450ed29b0c49c024e206ef6c8e662b1854e77a1c5faefed3b958be54f880", size = 3203, upload-time = "2025-09-08T20:41:28.438Z" }, -] - -[[package]] -name = "llama-index-vector-stores-milvus" -version = "0.9.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "pymilvus" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/79/72bf70fac3b77770a5c2b1b7d441aa1998bb522d50fe88b0f9c4071854e5/llama_index_vector_stores_milvus-0.9.6.tar.gz", hash = "sha256:6d38ac5939a570e0240687f54fbee4e1ff6c5faa2d28d25377a3f38d2ca07e2b", size = 15584, upload-time = "2026-01-13T11:46:41.394Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/04/98359849c095b5a3eb06f0649865a30b5a733b9fdbfeac3c6951253f804c/llama_index_vector_stores_milvus-0.9.6-py3-none-any.whl", hash = "sha256:916cbd9b07035ec137905970ef6a49dd77d3ece6e0a79271db35705cca5f5f84", size = 15792, upload-time = "2026-01-13T11:46:39.708Z" }, -] - -[[package]] -name = "llama-index-vector-stores-pinecone" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "pinecone" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/a0/2e2e969a133894f10b3a55b5148feef0c546ca8047b461f51f79d115c5b9/llama_index_vector_stores_pinecone-0.7.1.tar.gz", hash = "sha256:0ab3cc44f309bca1d74e58f221dade672169da01561114b067f4734293bd0280", size = 7852, upload-time = "2025-09-08T20:28:54.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/22/ae8c3073e4866a41eb53030db7cfecca1d84192c16a67d40a76f8d593e6d/llama_index_vector_stores_pinecone-0.7.1-py3-none-any.whl", hash = "sha256:861c4d01b3766cdca318f1285c03cd5e52dabf3d2f136cb38db421b16103129a", size = 8041, upload-time = "2025-09-08T20:28:53.406Z" }, -] - -[[package]] -name = "llama-index-vector-stores-postgres" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asyncpg" }, - { name = "llama-index-core" }, - { name = "pgvector" }, - { name = "psycopg2-binary" }, - { name = "sqlalchemy", extra = ["asyncio"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/78/04ff0cb9e14b8c1c3cb8716fab35c95ec2a4b551d769c65031c5c8624337/llama_index_vector_stores_postgres-0.7.3.tar.gz", hash = "sha256:7b5c62e462d681d7b8d8668b93e5b0023bfd3aaafcf76e2b4bfcf885dc3b49c6", size = 11950, upload-time = "2026-01-22T15:14:13.007Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/de/140f678d930ea869fc989adaa0140b9267c5ee0c7b971d061112d0d5b75a/llama_index_vector_stores_postgres-0.7.3-py3-none-any.whl", hash = "sha256:65b70266cc6041ab5011d64d1183d8783112ba5b38eb32ca21e00ea5b96aa058", size = 11635, upload-time = "2026-01-22T15:14:13.722Z" }, -] - -[[package]] -name = "llama-index-vector-stores-qdrant" -version = "0.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "llama-index-core" }, - { name = "qdrant-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/99/63/646ec9d7035429d4fb5488146ab7a9d55e955fb855f0a7e7fc29f6eb136f/llama_index_vector_stores_qdrant-0.9.1.tar.gz", hash = "sha256:215e24278bde44c6746d60c7df3f8811f943c20524a496e0c954eeb6449e8319", size = 14688, upload-time = "2026-01-13T11:13:07.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/d2/e258a2b5526fe42d6ca60a054b3830948d6411e3f075de542ba492c5f5d1/llama_index_vector_stores_qdrant-0.9.1-py3-none-any.whl", hash = "sha256:de71d0e867c04c87aa81ab35b092c32d684961c26a1cda601856faf29b21a598", size = 14944, upload-time = "2026-01-13T11:13:08.08Z" }, -] - -[[package]] -name = "llama-index-vector-stores-weaviate" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-core" }, - { name = "weaviate-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/75/bc95030d2c4a7c1c225a0035117442f4cd9fc1d975bde7abbfc4e94601a9/llama_index_vector_stores_weaviate-1.4.1.tar.gz", hash = "sha256:9fa3aa71732066f15349ddc796fbe230e74d345726db21d340334b0c4ebfdd3c", size = 8537, upload-time = "2025-09-08T20:45:46.877Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/cd/c70c0006a1d82f472700e6fcad9bad953f48405bb524c87888b8de215f7e/llama_index_vector_stores_weaviate-1.4.1-py3-none-any.whl", hash = "sha256:a7f32be4e1963b9718f9cbc12ac7f38db1a0a1041ba6fe45d809260a8d43f123", size = 9325, upload-time = "2025-09-08T20:45:45.845Z" }, -] - -[[package]] -name = "llama-index-workflows" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-index-instrumentation" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/7b/00c35d14dc4a7dc64c63dad8b1532f55cd5b5f8856c34f5bf587693ac270/llama_index_workflows-2.13.1.tar.gz", hash = "sha256:55cd3cff9c92a37272ab8651ad750288abc339165b009066f130cb0c5c65994b", size = 84279, upload-time = "2026-01-25T14:51:50.493Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/8d/ba97cda62829b60658a86d4c012d59e3b13c99d9b336fcf0e507d03812df/llama_index_workflows-2.13.1-py3-none-any.whl", hash = "sha256:e779078817d413b29a5297521fb71694a80e502f18dfd41e8b342f83e45f2c19", size = 107344, upload-time = "2026-01-25T14:51:49.297Z" }, -] - -[[package]] -name = "llama-parse" -version = "0.6.54" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llama-cloud-services" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/f6/93b5d123c480bc8c93e6dc3ea930f4f8df8da27f829bb011100ba3ce23dc/llama_parse-0.6.54.tar.gz", hash = "sha256:c707b31152155c9bae84e316fab790bbc8c85f4d8825ce5ee386ebeb7db258f1", size = 3577, upload-time = "2025-08-01T20:09:23.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/50/c5ccd2a50daa0a10c7f3f7d4e6992392454198cd8a7d99fcb96cb60d0686/llama_parse-0.6.54-py3-none-any.whl", hash = "sha256:c66c8d51cf6f29a44eaa8595a595de5d2598afc86e5a33a4cebe5fe228036920", size = 4879, upload-time = "2025-08-01T20:09:22.651Z" }, -] - -[[package]] -name = "llmwhisperer-client" -version = "2.6.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, - { name = "tenacity" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/44/18d4158618ebbd76ceb8e43b8deb77f4983e6f1ccff2dffd73d6f3fb1628/llmwhisperer_client-2.6.2.tar.gz", hash = "sha256:ce846af62e7e7337dfcfe2960ec72de2989457b717ab7b9dd4110ee82c002ed0", size = 3268197, upload-time = "2026-02-23T10:52:17.634Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/eb/0f9edd21302eddc6020a1b43a78e27f361e8b6b8af7611134a58487f7d8a/llmwhisperer_client-2.6.2-py3-none-any.whl", hash = "sha256:7226344506bc85a663e4d4f8feb763f853ec9bcb6cea9bd9cf170ba135c50cdd", size = 10857, upload-time = "2026-02-23T10:52:16.325Z" }, -] - -[[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" }, -] - -[[package]] -name = "marshmallow" -version = "3.26.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825, upload-time = "2025-02-03T15:32:25.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878, upload-time = "2025-02-03T15:32:22.295Z" }, -] - -[[package]] -name = "mbstrdecoder" -version = "1.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "chardet" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/31/ab/05ae008357c8bdb6245ebf8a101d99f26c096e0ea20800b318153da23796/mbstrdecoder-1.1.4.tar.gz", hash = "sha256:8105ef9cf6b7d7d69fe7fd6b68a2d8f281ca9b365d7a9b670be376b2e6c81b21", size = 14527, upload-time = "2025-01-18T10:07:31.089Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/ac/5ce64a1d4cce00390beab88622a290420401f1cabf05caf2fc0995157c21/mbstrdecoder-1.1.4-py3-none-any.whl", hash = "sha256:03dae4ec50ec0d2ff4743e63fdbd5e0022815857494d35224b60775d3d934a8c", size = 7933, upload-time = "2025-01-18T10:07:29.562Z" }, -] - -[[package]] -name = "milvus-lite" -version = "2.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b2/acc5024c8e8b6a0b034670b8e8af306ebd633ede777dcbf557eac4785937/milvus_lite-2.5.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6b014453200ba977be37ba660cb2d021030375fa6a35bc53c2e1d92980a0c512", size = 27934713, upload-time = "2025-06-30T04:23:37.028Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2e/746f5bb1d6facd1e73eb4af6dd5efda11125b0f29d7908a097485ca6cad9/milvus_lite-2.5.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a2e031088bf308afe5f8567850412d618cfb05a65238ed1a6117f60decccc95a", size = 24421451, upload-time = "2025-06-30T04:23:51.747Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cf/3d1fee5c16c7661cf53977067a34820f7269ed8ba99fe9cf35efc1700866/milvus_lite-2.5.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:a13277e9bacc6933dea172e42231f7e6135bd3bdb073dd2688ee180418abd8d9", size = 45337093, upload-time = "2025-06-30T04:24:06.706Z" }, - { url = "https://files.pythonhosted.org/packages/d3/82/41d9b80f09b82e066894d9b508af07b7b0fa325ce0322980674de49106a0/milvus_lite-2.5.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25ce13f4b8d46876dd2b7ac8563d7d8306da7ff3999bb0d14b116b30f71d706c", size = 55263911, upload-time = "2025-06-30T04:24:19.434Z" }, -] - -[[package]] -name = "msal" -version = "1.34.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "pyjwt", extra = ["crypto"] }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/0e/c857c46d653e104019a84f22d4494f2119b4fe9f896c92b4b864b3b045cc/msal-1.34.0.tar.gz", hash = "sha256:76ba83b716ea5a6d75b0279c0ac353a0e05b820ca1f6682c0eb7f45190c43c2f", size = 153961, upload-time = "2025-09-22T23:05:48.989Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/dc/18d48843499e278538890dc709e9ee3dea8375f8be8e82682851df1b48b5/msal-1.34.0-py3-none-any.whl", hash = "sha256:f669b1644e4950115da7a176441b0e13ec2975c29528d8b9e81316023676d6e1", size = 116987, upload-time = "2025-09-22T23:05:47.294Z" }, -] - -[[package]] -name = "msal-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "msal" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, -] - -[[package]] -name = "multidict" -version = "6.6.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516, upload-time = "2025-08-11T12:06:53.393Z" }, - { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394, upload-time = "2025-08-11T12:06:54.555Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591, upload-time = "2025-08-11T12:06:55.672Z" }, - { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215, upload-time = "2025-08-11T12:06:57.213Z" }, - { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299, upload-time = "2025-08-11T12:06:58.946Z" }, - { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357, upload-time = "2025-08-11T12:07:00.301Z" }, - { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369, upload-time = "2025-08-11T12:07:01.638Z" }, - { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341, upload-time = "2025-08-11T12:07:02.943Z" }, - { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100, upload-time = "2025-08-11T12:07:04.564Z" }, - { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584, upload-time = "2025-08-11T12:07:05.914Z" }, - { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018, upload-time = "2025-08-11T12:07:08.301Z" }, - { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477, upload-time = "2025-08-11T12:07:10.248Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575, upload-time = "2025-08-11T12:07:11.928Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649, upload-time = "2025-08-11T12:07:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505, upload-time = "2025-08-11T12:07:14.57Z" }, - { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888, upload-time = "2025-08-11T12:07:15.904Z" }, - { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072, upload-time = "2025-08-11T12:07:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222, upload-time = "2025-08-11T12:07:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" }, -] - -[[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 = "networkx" -version = "3.5" -source = { registry = "https://pypi.org/simple" } -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 = "nltk" -version = "3.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "joblib" }, - { name = "regex" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/87/db8be88ad32c2d042420b6fd9ffd4a149f9a0d7f0e86b3f543be2eeeedd2/nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868", size = 2904691, upload-time = "2024-08-18T19:48:37.769Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1", size = 1505442, upload-time = "2024-08-18T19:48:21.909Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, - { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, - { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, - { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, - { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, - { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, - { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, -] - -[[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 = "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.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/04/05040d7ce33a907a2a02257e601992f0cdf11c73b33f13c4492bf6c3d6d5/opentelemetry_api-1.37.0.tar.gz", hash = "sha256:540735b120355bd5112738ea53621f8d5edb35ebcd6fe21ada3ab1c61d1cd9a7", size = 64923, upload-time = "2025-09-11T10:29:01.662Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/48/28ed9e55dcf2f453128df738210a980e09f4e468a456fa3c763dbc8be70a/opentelemetry_api-1.37.0-py3-none-any.whl", hash = "sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47", size = 65732, upload-time = "2025-09-11T10:28:41.826Z" }, -] - -[[package]] -name = "opentelemetry-distro" -version = "0.58b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-instrumentation" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/20/597f387b42c649bac39af9ff8ad5bfdc163ce1a30cdecb16474ab8e57905/opentelemetry_distro-0.58b0.tar.gz", hash = "sha256:ef993c845c11fd156046a96e5ffe1ecfe33f7282fa6149cf9decb26ff8716666", size = 2583, upload-time = "2025-09-11T11:42:12.034Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/34/53016553489592262408b72e94466403da3c84ebe044b073bbcc1a6b228b/opentelemetry_distro-0.58b0-py3-none-any.whl", hash = "sha256:d90dddc3ae93d60d917a267a0099bd72f87fa3454b49ca8799f97cb58c777ef4", size = 3346, upload-time = "2025-09-11T11:40:56.853Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "opentelemetry-exporter-otlp-proto-http" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/bd/abafe13a0d77145270a39de7442d12d71b51a9f9d103d15d636110ae8a21/opentelemetry_exporter_otlp-1.15.0.tar.gz", hash = "sha256:4f7c49751d9720e2e726e13b0bb958ccade4e29122c305d92c033da432c8d2c5", size = 6126, upload-time = "2022-12-09T22:28:43.353Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/a2/4956610bd5348977fea8818d488793a46d1359337c0226164f093a17c61c/opentelemetry_exporter_otlp-1.15.0-py3-none-any.whl", hash = "sha256:79f22748b6a54808a0448093dfa189c8490e729f67c134d4c992533d9393b33e", size = 6976, upload-time = "2022-12-09T22:28:12.944Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/ab/1be294b194af410f350f867a54621b4f33b7551adce2ae795e907148fc1e/opentelemetry_exporter_otlp_proto_grpc-1.15.0.tar.gz", hash = "sha256:844f2a4bb9bcda34e4eb6fe36765e5031aacb36dc60ed88c90fc246942ea26e7", size = 27262, upload-time = "2022-12-09T22:28:44.359Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8f/73ad108bcfd61b4169be5ad8b76acaf9158f224740da10ab9ea3469d551a/opentelemetry_exporter_otlp_proto_grpc-1.15.0-py3-none-any.whl", hash = "sha256:c2a5492ba7d140109968135d641d06ce3c5bd73c50665f787526065d57d7fd1d", size = 20378, upload-time = "2022-12-09T22:28:14.623Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-http" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "googleapis-common-protos" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/ee/14baa8edbf6b0c8e23a93ee0807fb637d4689959a0b166e2821032fade34/opentelemetry_exporter_otlp_proto_http-1.15.0.tar.gz", hash = "sha256:11b2c814249a49b22f6cca7a06b05701f561d577b747f3660dfd67b6eb9daf9c", size = 18930, upload-time = "2022-12-09T22:28:45.366Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/12/77af459682a4f41eb9f13801af6a12420a86f5673dc568585ee49112e969/opentelemetry_exporter_otlp_proto_http-1.15.0-py3-none-any.whl", hash = "sha256:3ec2a02196c8a54bf5cbf7fe623a5238625638e83b6047a983bdf96e2bbb74c0", size = 21588, upload-time = "2022-12-09T22:28:15.776Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.58b0" -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/f6/36/7c307d9be8ce4ee7beb86d7f1d31027f2a6a89228240405a858d6e4d64f9/opentelemetry_instrumentation-0.58b0.tar.gz", hash = "sha256:df640f3ac715a3e05af145c18f527f4422c6ab6c467e40bd24d2ad75a00cb705", size = 31549, upload-time = "2025-09-11T11:42:14.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/db/5ff1cd6c5ca1d12ecf1b73be16fbb2a8af2114ee46d4b0e6d4b23f4f4db7/opentelemetry_instrumentation-0.58b0-py3-none-any.whl", hash = "sha256:50f97ac03100676c9f7fc28197f8240c7290ca1baa12da8bfbb9a1de4f34cc45", size = 33019, upload-time = "2025-09-11T11:41:00.624Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/80/b3b2a98039574e57b6b15982219ae025d55f8c46d50dde258865ce5601b4/opentelemetry_proto-1.15.0.tar.gz", hash = "sha256:9c4008e40ac8cab359daac283fbe7002c5c29c77ea2674ad5626a249e64e0101", size = 35713, upload-time = "2022-12-09T22:28:55.409Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/56/8343d94af8f32594f6b0bd273f72a40e430fb5970a353237af53af5d3031/opentelemetry_proto-1.15.0-py3-none-any.whl", hash = "sha256:044b6d044b4d10530f250856f933442b8753a17f94ae37c207607f733fb9a844", size = 52616, upload-time = "2022-12-09T22:28:30.03Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.37.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/f4/62/2e0ca80d7fe94f0b193135375da92c640d15fe81f636658d2acf373086bc/opentelemetry_sdk-1.37.0.tar.gz", hash = "sha256:cc8e089c10953ded765b5ab5669b198bbe0af1b3f89f1007d19acd32dc46dda5", size = 170404, upload-time = "2025-09-11T10:29:11.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/62/9f4ad6a54126fb00f7ed4bb5034964c6e4f00fcd5a905e115bd22707e20d/opentelemetry_sdk-1.37.0-py3-none-any.whl", hash = "sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c", size = 131941, upload-time = "2025-09-11T10:28:57.83Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.58b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/1b/90701d91e6300d9f2fb352153fb1721ed99ed1f6ea14fa992c756016e63a/opentelemetry_semantic_conventions-0.58b0.tar.gz", hash = "sha256:6bd46f51264279c433755767bb44ad00f1c9e2367e1b42af563372c5a6fa0c25", size = 129867, upload-time = "2025-09-11T10:29:12.597Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954, upload-time = "2025-09-11T10:28:59.218Z" }, -] - -[[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 = "pandas" -version = "2.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213, upload-time = "2024-09-20T13:10:04.827Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893, upload-time = "2024-09-20T13:09:09.655Z" }, - { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475, upload-time = "2024-09-20T13:09:14.718Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645, upload-time = "2024-09-20T19:02:03.88Z" }, - { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445, upload-time = "2024-09-20T13:09:17.621Z" }, - { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235, upload-time = "2024-09-20T19:02:07.094Z" }, - { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756, upload-time = "2024-09-20T13:09:20.474Z" }, - { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248, upload-time = "2024-09-20T13:09:23.137Z" }, -] - -[[package]] -name = "pastel" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555, upload-time = "2020-09-16T19:21:12.43Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" }, -] - -[[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 = "20250506" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "charset-normalizer" }, - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/46/5223d613ac4963e1f7c07b2660fe0e9e770102ec6bda8c038400113fb215/pdfminer_six-20250506.tar.gz", hash = "sha256:b03cc8df09cf3c7aba8246deae52e0bca7ebb112a38895b5e1d4f5dd2b8ca2e7", size = 7387678, upload-time = "2025-05-06T16:17:00.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/16/7a432c0101fa87457e75cb12c879e1749c5870a786525e2e0f42871d6462/pdfminer_six-20250506-py3-none-any.whl", hash = "sha256:d81ad173f62e5f841b53a8ba63af1a4a355933cfc0ffabd608e568b9193909e3", size = 5620187, upload-time = "2025-05-06T16:16:58.669Z" }, -] - -[[package]] -name = "pdfplumber" -version = "0.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pdfminer-six" }, - { name = "pillow" }, - { name = "pypdfium2" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/0d/4135821aa7b1a0b77a29fac881ef0890b46b0b002290d04915ed7acc0043/pdfplumber-0.11.7.tar.gz", hash = "sha256:fa67773e5e599de1624255e9b75d1409297c5e1d7493b386ce63648637c67368", size = 115518, upload-time = "2025-06-12T11:30:49.864Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/e0/52b67d4f00e09e497aec4f71bc44d395605e8ebcea52543242ed34c25ef9/pdfplumber-0.11.7-py3-none-any.whl", hash = "sha256:edd2195cca68bd770da479cf528a737e362968ec2351e62a6c0b71ff612ac25e", size = 60029, upload-time = "2025-06-12T11:30:48.89Z" }, -] - -[[package]] -name = "peewee" -version = "3.18.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/89/76f6f1b744c8608e0d416b588b9d63c2a500ff800065ae610f7c80f532d6/peewee-3.18.2.tar.gz", hash = "sha256:77a54263eb61aff2ea72f63d2eeb91b140c25c1884148e28e4c0f7c4f64996a0", size = 949220, upload-time = "2025-07-08T12:52:03.941Z" } - -[[package]] -name = "pgvector" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/43/9a0fb552ab4fd980680c2037962e331820f67585df740bedc4a2b50faf20/pgvector-0.4.1.tar.gz", hash = "sha256:83d3a1c044ff0c2f1e95d13dfb625beb0b65506cfec0941bfe81fd0ad44f4003", size = 30646, upload-time = "2025-04-26T18:56:37.151Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/21/b5735d5982892c878ff3d01bb06e018c43fc204428361ee9fc25a1b2125c/pgvector-0.4.1-py3-none-any.whl", hash = "sha256:34bb4e99e1b13d08a2fe82dda9f860f15ddcd0166fbb25bffe15821cbfeb7362", size = 27086, upload-time = "2025-04-26T18:56:35.956Z" }, -] - -[[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" }, -] - -[[package]] -name = "pinecone" -version = "7.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "pinecone-plugin-interface" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/9d/07a7f2136ce04cabd21d69c057dc2915867082b0047e6873e424388d4475/pinecone-7.0.1.tar.gz", hash = "sha256:49ff7b0f5be4a2ddec5aaa709758a9f2df56baa58ad46507d081409e246a81ec", size = 207930, upload-time = "2025-05-21T19:39:01.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/88/896221e991077d353e61991b759f46d75f3b4298eb5a4aa6534c1371f4b0/pinecone-7.0.1-py3-none-any.whl", hash = "sha256:ce7b0dab3c9f7d81e75b24c13fcbca4a51371e08021faaecaf0cd9a45ca1be6c", size = 516590, upload-time = "2025-05-21T19:38:59.117Z" }, -] - -[[package]] -name = "pinecone-plugin-interface" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/fb/e8a4063264953ead9e2b24d9b390152c60f042c951c47f4592e9996e57ff/pinecone_plugin_interface-0.0.7.tar.gz", hash = "sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846", size = 3370, upload-time = "2024-06-05T01:57:52.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/1d/a21fdfcd6d022cb64cef5c2a29ee6691c6c103c4566b41646b080b7536a5/pinecone_plugin_interface-0.0.7-py3-none-any.whl", hash = "sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8", size = 6249, upload-time = "2024-06-05T01:57:50.583Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, -] - -[[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 = "poethepoet" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pastel" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a5/f2/273fe54a78dc5c6c8dd63db71f5a6ceb95e4648516b5aeaeff4bde804e44/poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d", size = 68570, upload-time = "2025-08-11T18:00:29.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/1b/5337af1a6a478d25a3e3c56b9b4b42b0a160314e02f4a0498d5322c8dac4/poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22", size = 90062, upload-time = "2025-08-11T18:00:27.595Z" }, -] - -[[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 = "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/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/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 = "proto-plus" -version = "1.26.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, -] - -[[package]] -name = "protobuf" -version = "4.25.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/01/34c8d2b6354906d728703cb9d546a0e534de479e25f1b581e4094c4a85cc/protobuf-4.25.8.tar.gz", hash = "sha256:6135cf8affe1fc6f76cced2641e4ea8d3e59518d1f24ae41ba97bcad82d397cd", size = 380920, upload-time = "2025-05-28T14:22:25.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/ff/05f34305fe6b85bbfbecbc559d423a5985605cad5eda4f47eae9e9c9c5c5/protobuf-4.25.8-cp310-abi3-win32.whl", hash = "sha256:504435d831565f7cfac9f0714440028907f1975e4bed228e58e72ecfff58a1e0", size = 392745, upload-time = "2025-05-28T14:22:10.524Z" }, - { url = "https://files.pythonhosted.org/packages/08/35/8b8a8405c564caf4ba835b1fdf554da869954712b26d8f2a98c0e434469b/protobuf-4.25.8-cp310-abi3-win_amd64.whl", hash = "sha256:bd551eb1fe1d7e92c1af1d75bdfa572eff1ab0e5bf1736716814cdccdb2360f9", size = 413736, upload-time = "2025-05-28T14:22:13.156Z" }, - { url = "https://files.pythonhosted.org/packages/28/d7/ab27049a035b258dab43445eb6ec84a26277b16105b277cbe0a7698bdc6c/protobuf-4.25.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ca809b42f4444f144f2115c4c1a747b9a404d590f18f37e9402422033e464e0f", size = 394537, upload-time = "2025-05-28T14:22:14.768Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6d/a4a198b61808dd3d1ee187082ccc21499bc949d639feb948961b48be9a7e/protobuf-4.25.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:9ad7ef62d92baf5a8654fbb88dac7fa5594cfa70fd3440488a5ca3bfc6d795a7", size = 294005, upload-time = "2025-05-28T14:22:16.052Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c6/c9deaa6e789b6fc41b88ccbdfe7a42d2b82663248b715f55aa77fbc00724/protobuf-4.25.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:83e6e54e93d2b696a92cad6e6efc924f3850f82b52e1563778dfab8b355101b0", size = 294924, upload-time = "2025-05-28T14:22:17.105Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c1/6aece0ab5209981a70cd186f164c133fdba2f51e124ff92b73de7fd24d78/protobuf-4.25.8-py3-none-any.whl", hash = "sha256:15a0af558aa3b13efef102ae6e4f3efac06f1eea11afb3a57db2901447d9fb59", size = 156757, upload-time = "2025-05-28T14:22:24.135Z" }, -] - -[[package]] -name = "psycopg2-binary" -version = "2.9.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771, upload-time = "2024-10-16T11:20:35.234Z" }, - { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336, upload-time = "2024-10-16T11:20:38.742Z" }, - { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637, upload-time = "2024-10-16T11:20:42.145Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097, upload-time = "2024-10-16T11:20:46.185Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776, upload-time = "2024-10-16T11:20:50.879Z" }, - { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968, upload-time = "2024-10-16T11:20:56.819Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334, upload-time = "2024-10-16T11:21:02.411Z" }, - { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722, upload-time = "2024-10-16T11:21:09.01Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132, upload-time = "2024-10-16T11:21:16.339Z" }, - { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312, upload-time = "2024-10-16T11:21:25.584Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191, upload-time = "2024-10-16T11:21:29.912Z" }, - { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" }, -] - -[[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 = "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.11.9" -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/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, -] - -[[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/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" }, -] - -[[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 = "pymilvus" -version = "2.5.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "milvus-lite", marker = "sys_platform != 'win32'" }, - { name = "pandas" }, - { name = "protobuf" }, - { name = "python-dotenv" }, - { name = "setuptools" }, - { name = "ujson" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/24/e2/5613bc7b2af0ccd760177ca4255243c284cfc0f2cba3f10ff63325c4ca34/pymilvus-2.5.16.tar.gz", hash = "sha256:65f56b81806bc217cca3cf29b70a27d053dea4b1ffada910cf63a38f96381618", size = 1280614, upload-time = "2025-09-19T07:02:14.747Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/09/b67a55abee0a53ea50ba0de0cba6e1c0f7ca7ce2c15ffd6f40c059c25e88/pymilvus-2.5.16-py3-none-any.whl", hash = "sha256:76258a324f19c60fee247467e11cd7d6f35a64d2a9c753f5d7b1a5fa15dd6c8a", size = 243272, upload-time = "2025-09-19T07:02:12.443Z" }, -] - -[[package]] -name = "pypdf" -version = "6.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/85/4c0f12616db83c2e3ef580c3cfa98bd082e88fc8d02e136bad3bede1e3fa/pypdf-6.1.1.tar.gz", hash = "sha256:10f44d49bf2a82e54c3c5ba3cdcbb118f2a44fc57df8ce51d6fb9b1ed9bfbe8b", size = 5074507, upload-time = "2025-09-28T13:29:16.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/ed/adae13756d9dabdddee483fc7712905bb5585fbf6e922b1a19aca3a29cd1/pypdf-6.1.1-py3-none-any.whl", hash = "sha256:7781f99493208a37a7d4275601d883e19af24e62a525c25844d22157c2e4cde7", size = 323455, upload-time = "2025-09-28T13:29:14.392Z" }, -] - -[[package]] -name = "pypdfium2" -version = "4.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, - { url = "https://files.pythonhosted.org/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, - { url = "https://files.pythonhosted.org/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, - { url = "https://files.pythonhosted.org/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, - { url = "https://files.pythonhosted.org/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, - { url = "https://files.pythonhosted.org/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, - { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, -] - -[[package]] -name = "pytablewriter" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dataproperty" }, - { name = "mbstrdecoder" }, - { name = "pathvalidate" }, - { name = "setuptools" }, - { name = "tabledata" }, - { name = "tcolorpy" }, - { name = "typepy", extra = ["datetime"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/a1/617730f290f04d347103ab40bf67d317df6691b14746f6e1ea039fb57062/pytablewriter-1.2.1.tar.gz", hash = "sha256:7bd0f4f397e070e3b8a34edcf1b9257ccbb18305493d8350a5dbc9957fced959", size = 619241, upload-time = "2025-01-01T15:37:00.04Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/4c/c199512f01c845dfe5a7840ab3aae6c60463b5dc2a775be72502dfd9170a/pytablewriter-1.2.1-py3-none-any.whl", hash = "sha256:e906ff7ff5151d70a5f66e0f7b75642a7f2dce8d893c265b79cc9cf6bc04ddb4", size = 91083, upload-time = "2025-01-01T15:36:55.63Z" }, -] - -[[package]] -name = "pytest" -version = "8.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/c0/238f25cb27495fdbaa5c48cef9886162e9df1f3d0e957fc8326d9c24fa2f/pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd", size = 1396924, upload-time = "2024-02-24T22:21:30.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/ea/d0ab9595a0d4b2320483e634123171deaf50885e29d442180efcbf2ed0b2/pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096", size = 333984, upload-time = "2024-02-24T22:21:27.561Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "0.23.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/b4/0b378b7bf26a8ae161c3890c0b48a91a04106c5713ce81b4b080ea2f4f18/pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3", size = 46920, upload-time = "2024-07-17T17:39:34.617Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/82/62e2d63639ecb0fbe8a7ee59ef0bc69a4669ec50f6d3459f74ad4e4189a2/pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", size = 17663, upload-time = "2024-07-17T17:39:32.478Z" }, -] - -[[package]] -name = "pytest-dotenv" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/b0/cafee9c627c1bae228eb07c9977f679b3a7cb111b488307ab9594ba9e4da/pytest-dotenv-0.5.2.tar.gz", hash = "sha256:2dc6c3ac6d8764c71c6d2804e902d0ff810fa19692e95fe138aefc9b1aa73732", size = 3782, upload-time = "2020-06-16T12:38:03.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/da/9da67c67b3d0963160e3d2cbc7c38b6fae342670cc8e6d5936644b2cf944/pytest_dotenv-0.5.2-py3-none-any.whl", hash = "sha256:40a2cece120a213898afaa5407673f6bd924b1fa7eafce6bda0e8abffe2f710f", size = 3993, upload-time = "2020-06-16T12:38:01.139Z" }, -] - -[[package]] -name = "pytest-md-report" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytablewriter" }, - { name = "pytest" }, - { name = "tcolorpy" }, - { name = "typepy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/63/80d92406f952eee7856114c18ad269192ce179d576343fbcf0679c9a2bdd/pytest_md_report-0.7.0.tar.gz", hash = "sha256:3b832eaf660b470b5742e58d9c9a5e312b0712a7012d251cc04e908a81ce3c96", size = 284275, upload-time = "2025-05-02T03:07:09.835Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/96/6125a1a963b3864b4a3981c9fce27df60370a889c2e79071e33d325f14d9/pytest_md_report-0.7.0-py3-none-any.whl", hash = "sha256:90ccb3b5b9587d064ec83974db34619addd11d3de6ec7056fab4feb3af69ae94", size = 14250, upload-time = "2025-05-02T03:07:06.539Z" }, -] - -[[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.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, -] - -[[package]] -name = "python-magic" -version = "0.4.27" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, -] - -[[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" }, -] - -[[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" }, -] - -[[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 = "redis" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/da/d283a37303a995cd36f8b92db85135153dc4f7a8e4441aa827721b442cfb/redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f", size = 4608355, upload-time = "2024-12-06T09:50:41.956Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/5f/fa26b9b2672cbe30e07d9a5bdf39cf16e3b80b42916757c5f92bca88e4ba/redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4", size = 261502, upload-time = "2024-12-06T09:50:39.656Z" }, -] - -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions" }, -] -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.9.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/d3/eaa0d28aba6ad1827ad1e716d9a93e1ba963ada61887498297d3da715133/regex-2025.9.18.tar.gz", hash = "sha256:c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4", size = 400917, upload-time = "2025-09-19T00:38:35.79Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/99/05859d87a66ae7098222d65748f11ef7f2dff51bfd7482a4e2256c90d72b/regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e", size = 486335, upload-time = "2025-09-19T00:36:03.661Z" }, - { url = "https://files.pythonhosted.org/packages/97/7e/d43d4e8b978890932cf7b0957fce58c5b08c66f32698f695b0c2c24a48bf/regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a", size = 289720, upload-time = "2025-09-19T00:36:05.471Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3b/ff80886089eb5dcf7e0d2040d9aaed539e25a94300403814bb24cc775058/regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab", size = 287257, upload-time = "2025-09-19T00:36:07.072Z" }, - { url = "https://files.pythonhosted.org/packages/ee/66/243edf49dd8720cba8d5245dd4d6adcb03a1defab7238598c0c97cf549b8/regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5", size = 797463, upload-time = "2025-09-19T00:36:08.399Z" }, - { url = "https://files.pythonhosted.org/packages/df/71/c9d25a1142c70432e68bb03211d4a82299cd1c1fbc41db9409a394374ef5/regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742", size = 862670, upload-time = "2025-09-19T00:36:10.101Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8f/329b1efc3a64375a294e3a92d43372bf1a351aa418e83c21f2f01cf6ec41/regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425", size = 910881, upload-time = "2025-09-19T00:36:12.223Z" }, - { url = "https://files.pythonhosted.org/packages/35/9e/a91b50332a9750519320ed30ec378b74c996f6befe282cfa6bb6cea7e9fd/regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352", size = 802011, upload-time = "2025-09-19T00:36:13.901Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1d/6be3b8d7856b6e0d7ee7f942f437d0a76e0d5622983abbb6d21e21ab9a17/regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d", size = 786668, upload-time = "2025-09-19T00:36:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ce/4a60e53df58bd157c5156a1736d3636f9910bdcc271d067b32b7fcd0c3a8/regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56", size = 856578, upload-time = "2025-09-19T00:36:16.845Z" }, - { url = "https://files.pythonhosted.org/packages/86/e8/162c91bfe7217253afccde112868afb239f94703de6580fb235058d506a6/regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e", size = 849017, upload-time = "2025-09-19T00:36:18.597Z" }, - { url = "https://files.pythonhosted.org/packages/35/34/42b165bc45289646ea0959a1bc7531733e90b47c56a72067adfe6b3251f6/regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282", size = 788150, upload-time = "2025-09-19T00:36:20.464Z" }, - { url = "https://files.pythonhosted.org/packages/79/5d/cdd13b1f3c53afa7191593a7ad2ee24092a5a46417725ffff7f64be8342d/regex-2025.9.18-cp312-cp312-win32.whl", hash = "sha256:e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459", size = 264536, upload-time = "2025-09-19T00:36:21.922Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f5/4a7770c9a522e7d2dc1fa3ffc83ab2ab33b0b22b447e62cffef186805302/regex-2025.9.18-cp312-cp312-win_amd64.whl", hash = "sha256:3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77", size = 275501, upload-time = "2025-09-19T00:36:23.4Z" }, - { url = "https://files.pythonhosted.org/packages/df/05/9ce3e110e70d225ecbed455b966003a3afda5e58e8aec2964042363a18f4/regex-2025.9.18-cp312-cp312-win_arm64.whl", hash = "sha256:032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5", size = 268601, upload-time = "2025-09-19T00:36:25.092Z" }, -] - -[[package]] -name = "requests" -version = "2.31.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794, upload-time = "2023-05-22T15:12:44.175Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574, upload-time = "2023-05-22T15:12:42.313Z" }, -] - -[[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 = "rpds-py" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, -] - -[[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 = "s3fs" -version = "2024.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiobotocore" }, - { name = "aiohttp" }, - { name = "fsspec" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/65/4b4c868cff76c036d11dc75dd91e5696dbf16ce626514166f35d5f4a930f/s3fs-2024.10.0.tar.gz", hash = "sha256:58b8c3650f8b99dbedf361543da3533aac8707035a104db5d80b094617ad4a3f", size = 75916, upload-time = "2024-10-21T01:45:49.967Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/44/bb9ff095ae7b1b6908480f683b6ca6b71c2105d343a5e5cb25334b01f5fa/s3fs-2024.10.0-py3-none-any.whl", hash = "sha256:7a2025d60d5b1a6025726b3a5e292a8e5aa713abc3b16fd1f81735181f7bb282", size = 29855, upload-time = "2024-10-21T01:45:47.905Z" }, -] - -[package.optional-dependencies] -boto3 = [ - { name = "aiobotocore", extra = ["boto3"] }, -] - -[[package]] -name = "s3transfer" -version = "0.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "botocore" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/0a/1cdbabf9edd0ea7747efdf6c9ab4e7061b085aa7f9bfc36bb1601563b069/s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7", size = 145287, upload-time = "2024-11-20T21:06:05.981Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/66/05/7957af15543b8c9799209506df4660cba7afc4cf94bfb60513827e96bed6/s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", size = 83175, upload-time = "2024-11-20T21:06:03.961Z" }, -] - -[[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 = "singleton-decorator" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/98/a8b5c919bee1152a9a1afd82014431f8db5882699754de50d1b3aba4d136/singleton-decorator-1.0.0.tar.gz", hash = "sha256:1a90ad8a8a738be591c9c167fdd677c5d4a43d1bc6b1c128227be1c5e03bee07", size = 2791, upload-time = "2017-08-10T19:52:45.903Z" } - -[[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 = "soupsieve" -version = "2.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, -] - -[[package]] -name = "sqlalchemy" -version = "2.0.43" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891, upload-time = "2025-08-11T15:51:13.019Z" }, - { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061, upload-time = "2025-08-11T15:51:14.319Z" }, - { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384, upload-time = "2025-08-11T15:52:35.088Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648, upload-time = "2025-08-11T15:56:34.153Z" }, - { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030, upload-time = "2025-08-11T15:52:36.933Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469, upload-time = "2025-08-11T15:56:35.553Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906, upload-time = "2025-08-11T15:55:00.645Z" }, - { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260, upload-time = "2025-08-11T15:55:02.965Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, -] - -[package.optional-dependencies] -asyncio = [ - { name = "greenlet" }, -] - -[[package]] -name = "striprtf" -version = "0.0.26" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/20/3d419008265346452d09e5dadfd5d045b64b40d8fc31af40588e6c76997a/striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa", size = 6258, upload-time = "2023-07-20T14:30:36.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/cf/0fea4f4ba3fc2772ac2419278aa9f6964124d4302117d61bc055758e000c/striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb", size = 6914, upload-time = "2023-07-20T14:30:35.338Z" }, -] - -[[package]] -name = "tabledata" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dataproperty" }, - { name = "typepy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/35/171c8977162f1163368406deddde4c59673b62bd0cb2f34948a02effb075/tabledata-1.3.4.tar.gz", hash = "sha256:e9649cab129d718f3bff4150083b77f8a78c30f6634a30caf692b10fdc60cb97", size = 25074, upload-time = "2024-12-31T14:12:31.198Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/64/fa4160151976ee4b2cf0c1217a99443ffaeb991956feddfeac9eee9952f8/tabledata-1.3.4-py3-none-any.whl", hash = "sha256:1f56e433bfdeb89f4487abfa48c4603a3b07c5d3a3c7e05ff73dd018c24bd0d4", size = 11820, upload-time = "2024-12-31T14:12:28.584Z" }, -] - -[[package]] -name = "tcolorpy" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/cc/44f2d81d8f9093aad81c3467a5bf5718d2b5f786e887b6e4adcfc17ec6b9/tcolorpy-0.1.7.tar.gz", hash = "sha256:0fbf6bf238890bbc2e32662aa25736769a29bf6d880328f310c910a327632614", size = 299437, upload-time = "2024-12-29T15:24:23.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/a2/ed023f2edd1e011b4d99b6727bce8253842d66c3fbf9ed0a26fc09a92571/tcolorpy-0.1.7-py3-none-any.whl", hash = "sha256:26a59d52027e175a37e0aba72efc99dda43f074db71f55b316d3de37d3251378", size = 8096, upload-time = "2024-12-29T15:24:21.33Z" }, -] - -[[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 = "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/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" }, -] - -[[package]] -name = "tokenizers" -version = "0.15.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91", size = 320256, upload-time = "2024-02-12T02:28:50.62Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670", size = 2574444, upload-time = "2024-02-12T02:25:27.417Z" }, - { url = "https://files.pythonhosted.org/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51", size = 2411608, upload-time = "2024-02-12T02:25:29.74Z" }, - { url = "https://files.pythonhosted.org/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98", size = 3652367, upload-time = "2024-02-12T02:25:32.079Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66", size = 3529509, upload-time = "2024-02-12T02:25:34.042Z" }, - { url = "https://files.pythonhosted.org/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd", size = 3396516, upload-time = "2024-02-12T02:25:35.884Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38", size = 3918811, upload-time = "2024-02-12T02:25:37.85Z" }, - { url = "https://files.pythonhosted.org/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c", size = 4025494, upload-time = "2024-02-12T02:25:40.247Z" }, - { url = "https://files.pythonhosted.org/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456", size = 3575314, upload-time = "2024-02-12T02:25:42.212Z" }, - { url = "https://files.pythonhosted.org/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834", size = 9682779, upload-time = "2024-02-12T02:25:44.027Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d", size = 9995614, upload-time = "2024-02-12T02:25:46.804Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b", size = 2011030, upload-time = "2024-02-12T02:25:49.829Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221", size = 2180523, upload-time = "2024-02-12T02:25:51.542Z" }, -] - -[[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 = "typepy" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mbstrdecoder" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/59/4c39942077d7de285f762a91024dbda731be693591732977358f77d120fb/typepy-1.3.4.tar.gz", hash = "sha256:89c1f66de6c6133209c43a94d23431d320ba03ef5db18f241091ea594035d9de", size = 39558, upload-time = "2024-12-29T09:18:15.774Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/31/e393c3830bdedd01735bd195c85ac3034b6bcaf6c18142bab60a4047ca36/typepy-1.3.4-py3-none-any.whl", hash = "sha256:d5ed3e0c7f49521bff0603dd08cf8d453371cf68d65a29d3d0038552ccc46e2e", size = 31449, upload-time = "2024-12-29T09:18:13.135Z" }, -] - -[package.optional-dependencies] -datetime = [ - { name = "packaging" }, - { name = "python-dateutil" }, - { name = "pytz" }, -] - -[[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-inspect" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" }, -] - -[[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 = "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" }, -] - -[[package]] -name = "unstract-core" -version = "0.0.1" -source = { editable = "../unstract/core" } -dependencies = [ - { name = "httpx" }, - { name = "kombu" }, - { name = "redis" }, - { name = "requests" }, -] - -[package.metadata] -requires-dist = [ - { name = "flask", marker = "extra == 'flask'", specifier = "~=3.1.0" }, - { name = "httpx", specifier = ">=0.27.0" }, - { name = "kombu", specifier = "~=5.5.3" }, - { name = "redis", specifier = "~=5.2.1" }, - { name = "requests", specifier = "==2.31.0" }, -] -provides-extras = ["flask"] - -[[package]] -name = "unstract-flags" -version = "0.0.1" -source = { editable = "../unstract/flags" } -dependencies = [ - { name = "grpcio" }, - { name = "grpcio-tools" }, - { name = "protobuf" }, -] - -[package.metadata] -requires-dist = [ - { name = "grpcio", specifier = ">=1.60.0" }, - { name = "grpcio-tools", specifier = ">=1.60.0" }, - { name = "protobuf", specifier = ">=4.25.0" }, -] - -[[package]] -name = "unstract-prompt-service" -version = "0.0.1" -source = { editable = "." } -dependencies = [ - { name = "flask" }, - { name = "json-repair" }, - { name = "llama-index" }, - { name = "nltk" }, - { name = "peewee" }, - { name = "python-dotenv" }, - { name = "redis" }, - { name = "requests" }, - { name = "unstract-core" }, - { name = "unstract-flags" }, - { name = "unstract-sdk1", extra = ["aws", "azure", "gcs"] }, -] - -[package.dev-dependencies] -deploy = [ - { name = "gunicorn" }, - { name = "opentelemetry-distro" }, - { name = "opentelemetry-exporter-otlp" }, -] -dev = [ - { name = "debugpy" }, - { name = "poethepoet" }, -] -test = [ - { name = "flask-wtf" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-dotenv" }, - { name = "pytest-md-report" }, - { name = "pytest-mock" }, - { name = "python-dotenv" }, -] - -[package.metadata] -requires-dist = [ - { name = "flask", specifier = "~=3.0" }, - { name = "json-repair", specifier = "~=0.42.0" }, - { name = "llama-index", specifier = ">=0.14.13" }, - { name = "nltk", specifier = "~=3.8" }, - { name = "peewee", specifier = "~=3.16" }, - { name = "python-dotenv", specifier = "==1.0.1" }, - { name = "redis", specifier = ">=5.0.3,<5.3" }, - { name = "requests", specifier = ">=2.28,<3.0" }, - { name = "unstract-core", editable = "../unstract/core" }, - { name = "unstract-flags", editable = "../unstract/flags" }, - { name = "unstract-sdk1", extras = ["aws", "gcs", "azure"], editable = "../unstract/sdk1" }, -] - -[package.metadata.requires-dev] -deploy = [ - { name = "gunicorn", specifier = "~=23.0" }, - { name = "opentelemetry-distro" }, - { name = "opentelemetry-exporter-otlp" }, -] -dev = [ - { name = "debugpy", specifier = ">=1.8.14" }, - { name = "poethepoet", specifier = ">=0.33.1" }, -] -test = [ - { name = "flask-wtf", specifier = "~=1.1" }, - { name = "pytest", specifier = "~=8.0.1" }, - { name = "pytest-asyncio", specifier = ">=0.23.0" }, - { name = "pytest-dotenv", specifier = "==0.5.2" }, - { name = "pytest-md-report", specifier = ">=0.6.2" }, - { name = "pytest-mock", specifier = "~=3.14.0" }, - { name = "python-dotenv", specifier = "==1.0.1" }, -] - -[[package]] -name = "unstract-sdk1" -source = { editable = "../unstract/sdk1" } -dependencies = [ - { name = "filetype" }, - { name = "httpx" }, - { name = "jsonschema" }, - { name = "litellm" }, - { name = "llama-index" }, - { name = "llama-index-vector-stores-milvus" }, - { name = "llama-index-vector-stores-pinecone" }, - { name = "llama-index-vector-stores-postgres" }, - { name = "llama-index-vector-stores-qdrant" }, - { name = "llama-index-vector-stores-weaviate" }, - { name = "llama-parse" }, - { name = "llmwhisperer-client" }, - { name = "pdfplumber" }, - { name = "python-dotenv" }, - { name = "python-magic" }, - { name = "qdrant-client" }, - { name = "redis" }, - { name = "singleton-decorator" }, - { name = "tiktoken" }, - { name = "unstract-core" }, -] - -[package.optional-dependencies] -aws = [ - { name = "boto3" }, - { name = "s3fs", extra = ["boto3"] }, -] -azure = [ - { name = "adlfs" }, -] -gcs = [ - { name = "gcsfs" }, -] - -[package.metadata] -requires-dist = [ - { name = "adlfs", marker = "extra == 'azure'", specifier = "~=2024.7.0" }, - { name = "boto3", marker = "extra == 'aws'", specifier = "~=1.34.131" }, - { name = "filetype", specifier = "~=1.2.0" }, - { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, - { name = "httpx", specifier = ">=0.25.2" }, - { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, - { name = "llama-index", specifier = ">=0.14.13" }, - { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, - { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, - { name = "llama-index-vector-stores-postgres", specifier = ">=0.7.3" }, - { name = "llama-index-vector-stores-qdrant", specifier = ">=0.9.1" }, - { name = "llama-index-vector-stores-weaviate", specifier = ">=1.4.1" }, - { name = "llama-parse", specifier = ">=0.6.0" }, - { name = "llmwhisperer-client", specifier = ">=2.6.2" }, - { name = "pdfplumber", specifier = ">=0.11.2" }, - { name = "python-dotenv", specifier = "==1.0.1" }, - { name = "python-magic", specifier = "~=0.4.27" }, - { name = "qdrant-client", specifier = ">=1.16.0,<1.17.0" }, - { name = "redis", specifier = ">=5.2.1" }, - { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, - { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, - { name = "unstract-core", editable = "../unstract/core" }, -] -provides-extras = ["aws", "azure", "gcs"] - -[package.metadata.requires-dev] -dev = [ - { name = "docutils", specifier = "~=0.20.1" }, - { name = "mypy", specifier = "~=1.2.0" }, - { name = "pre-commit", specifier = "~=3.3.1" }, - { name = "pycln", specifier = ">=2.5.0" }, - { name = "pytest", specifier = ">=8.0.1" }, - { name = "ruff", specifier = ">=0.2.2,<1.0.0" }, - { name = "yamllint", specifier = ">=1.35.1" }, -] -docs = [{ name = "lazydocs", specifier = "~=0.4.8" }] -test = [ - { name = "parameterized", specifier = "==0.9.0" }, - { name = "pytest", specifier = "==8.3.3" }, - { name = "pytest-asyncio", specifier = ">=0.24.0" }, - { name = "pytest-cov", specifier = ">=6.0.0" }, - { name = "pytest-md-report", specifier = ">=0.6.2" }, - { name = "pytest-mock", specifier = "==3.14.0" }, -] - -[[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 = "validators" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399, upload-time = "2025-05-01T05:42:06.7Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712, upload-time = "2025-05-01T05:42:04.203Z" }, -] - -[[package]] -name = "vine" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980, upload-time = "2023-11-05T08:46:53.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636, upload-time = "2023-11-05T08:46:51.205Z" }, -] - -[[package]] -name = "weaviate-client" -version = "4.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "deprecation" }, - { name = "grpcio" }, - { name = "httpx" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "validators" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/0e/e4582b007427187a9fde55fa575db4b766c81929d2b43a3dd8becce50567/weaviate_client-4.17.0.tar.gz", hash = "sha256:731d58d84b0989df4db399b686357ed285fb95971a492ccca8dec90bb2343c51", size = 769019, upload-time = "2025-09-26T11:20:27.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/c5/2da3a45866da7a935dab8ad07be05dcaee48b3ad4955144583b651929be7/weaviate_client-4.17.0-py3-none-any.whl", hash = "sha256:60e4a355b90537ee1e942ab0b76a94750897a13d9cf13c5a6decbd166d0ca8b5", size = 582763, upload-time = "2025-09-26T11:20:25.864Z" }, -] - -[[package]] -name = "werkzeug" -version = "3.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" }, -] - -[[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/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 = "wtforms" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/e4/633d080897e769ed5712dcfad626e55dbd6cf45db0ff4d9884315c6a82da/wtforms-3.2.1.tar.gz", hash = "sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682", size = 137801, upload-time = "2024-10-21T11:34:00.108Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/c9/2088fb5645cd289c99ebe0d4cdcc723922a1d8e1beaefb0f6f76dff9b21c/wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4", size = 152454, upload-time = "2024-10-21T11:33:58.44Z" }, -] - -[[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/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/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/tools/classifier/.dockerignore b/tools/classifier/.dockerignore deleted file mode 100644 index c26352afcc..0000000000 --- a/tools/classifier/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -venv/ -.venv/ -.env diff --git a/tools/classifier/Dockerfile b/tools/classifier/Dockerfile deleted file mode 100644 index f82cff2675..0000000000 --- a/tools/classifier/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM python:3.12-slim-trixie - -LABEL maintainer="Zipstack Inc." - -ENV \ - # Extended PYTHONPATH to include all unstract module source directories - APP_HOME=/app \ - BUILD_PACKAGES_PATH=unstract \ - # Increase timeout for large packages (flipt-client is ~45MB) - PIP_DEFAULT_TIMEOUT=120 - -# Install dependencies for unstructured library's partition -RUN apt-get update && apt-get --no-install-recommends -y install dumb-init git libmagic-dev poppler-utils\ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install --no-cache-dir -U pip -# Set the working directory in the container -WORKDIR ${APP_HOME} -COPY tools/classifier/requirements.txt /app/ - -# Copy specific subdirectories while preserving structure -COPY ${BUILD_PACKAGES_PATH}/sdk1 /unstract/sdk1 -COPY ${BUILD_PACKAGES_PATH}/core /unstract/core -COPY ${BUILD_PACKAGES_PATH}/flags /unstract/flags - -RUN pip install --no-cache-dir -r requirements.txt && \ - pip install --no-cache-dir \ - opentelemetry-distro \ - opentelemetry-exporter-otlp \ - platformdirs>=3.0.0 \ - && pip install opentelemetry-instrumentation-openai \ - && opentelemetry-bootstrap -a install \ - && pip uninstall -y opentelemetry-instrumentation-openai-v2 - -# Copy the contents of your project directory into the container at /app -COPY tools/classifier/src /app/src/ -WORKDIR /app/src - - -ENTRYPOINT ["opentelemetry-instrument","python", "main.py"] diff --git a/tools/classifier/README.md b/tools/classifier/README.md deleted file mode 100644 index 34dd5a88c4..0000000000 --- a/tools/classifier/README.md +++ /dev/null @@ -1,120 +0,0 @@ -# Document Classifier Tool - -The document classifier tool classifies documents and copies them to a folder based on the classification. - -## Required environment variables - -| Variable | Description | -| -------------------------- |-----------------------------------------------------------------------| -| `PLATFORM_SERVICE_HOST` | The host in which the platform service is running | -| `PLATFORM_SERVICE_PORT` | The port in which the service is listening | -| `PLATFORM_SERVICE_API_KEY` | The API key for the platform | -| `EXECUTION_DATA_DIR` | The directory in the filesystem which has contents for tool execution | - -## Testing the tool locally - -### Setting up a dev environment - -Setup a virtual environment and activate it - -```commandline -python -m venv .venv -source .venv/bin/activate -``` - -Install the dependencies for the tool - -```commandline -pip install -r requirements.txt -``` - -To use the local development version of the [unstract-sdk](https://pypi.org/project/unstract-sdk/) install it from the local repository. -Replace the path with the path to your local repository - -```commandline -pip install -e ~/path_to_repo/sdks/. -``` - -### Tool execution preparation - -Load the environment variables for the tool. -Make a copy of the `sample.env` file and name it `.env`. Fill in the required values. -They get loaded with [python-dotenv](https://pypi.org/project/python-dotenv/) through the SDK. - -Update the tool's `data_dir` marked by the `EXECUTION_DATA_DIR` env. This has to be done before each tool execution since the tool updates the `INFILE` and `METADATA.json`. - -### Run SPEC command - -Represents the JSON schema for the runtime configurable `settings` of a tool - -```commandline -python main.py --command SPEC -``` - -### Run PROPERTIES command - -Describes some metadata for the tool such as its `version`, `description`, `inputs` and `outputs` - -```commandline -python main.py --command PROPERTIES -``` - -### Run ICON command - -Returns the SVG icon for the tool, used by Unstract's frontend - -```commandline -python main.py --command ICON -``` - -### Run VARIABLES command - -Represents the runtime variables or envs that will be used by the tool - -```commandline -python main.py --command VARIABLES -``` - -### Run RUN command - -The schema of the JSON required for settings can be found by running the [SPEC](#run-spec-command) command. Alternatively if you have access to the code base, it is located in the `config` folder as `spec.json`. - -```commandline -python main.py \ - --command RUN \ - --settings '{ - "llmAdapterId": "e9884c72-3920-43e7-9904-bb59f308f50d", - "classificationBins": ["business", "sports", "politics", "entertainment", "tech"], - "useCache": true - }' \ - --log-level DEBUG - -``` - -## Testing the tool from its docker image - -Build the tool docker image from the folder containing the `Dockerfile` with - -```commandline -docker build -t unstract/tool-classifier:0.0.1 . -``` - -Make sure the directory pointed by `EXECUTION_DATA_DIR` has the required information for the tool to run and -necessary services like the `platform-service` is up. -To test the tool from its docker image, run the following command - -```commandline -docker run -it \ - --network unstract-network \ - --env-file .env \ - -v "$(pwd)"/data_dir:/app/data_dir \ - unstract/tool-classifier:0.0.1 \ - --command RUN \ - --settings '{ - "llmAdapterId": "e9884c72-3920-43e7-9904-bb59f308f50d", - "classificationBins": ["business", "sports", "politics", "entertainment", "tech"], - "useCache": true - }' \ - --log-level DEBUG - -``` diff --git a/tools/classifier/__init__.py b/tools/classifier/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/classifier/requirements.txt b/tools/classifier/requirements.txt deleted file mode 100644 index 4347f319b3..0000000000 --- a/tools/classifier/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Add your dependencies here - -# Required for all unstract tools -# aws alone is needed here -# because tools use transient temporary storage. - --e file:/unstract/core --e file:/unstract/sdk1[aws] --e file:/unstract/flags diff --git a/tools/classifier/sample.env b/tools/classifier/sample.env deleted file mode 100644 index b1d6474d75..0000000000 --- a/tools/classifier/sample.env +++ /dev/null @@ -1,14 +0,0 @@ -PLATFORM_SERVICE_HOST=http://unstract-platform-service -PLATFORM_SERVICE_PORT=3001 -PLATFORM_SERVICE_API_KEY= -EXECUTION_DATA_DIR=../data_dir - -X2TEXT_HOST=http://unstract-x2text-service -X2TEXT_PORT=3004 - -# File System Configuration for Workflow Execution -# Directory path for execution data storage -# (e.g., bucket/execution/org_id/workflow_id/execution_id) -EXECUTION_DATA_DIR= -# Storage provider for Workflow Execution (e.g., minio, S3) -WORKFLOW_EXECUTION_FILE_STORAGE_CREDENTIALS='{"provider":"minio","credentials"={"endpoint_url":"http://localhost:9000","key":"XXX","secret":"XXX"}}' diff --git a/tools/classifier/src/config/icon.svg b/tools/classifier/src/config/icon.svg deleted file mode 100644 index e3a507bafa..0000000000 --- a/tools/classifier/src/config/icon.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tools/classifier/src/config/properties.json b/tools/classifier/src/config/properties.json deleted file mode 100644 index 1b7b306e3b..0000000000 --- a/tools/classifier/src/config/properties.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "schemaVersion": "0.0.1", - "displayName": "File Classifier", - "functionName": "classify", - "toolVersion": "0.0.79", - "description": "Classifies a file into a bin based on its contents", - "input": { - "description": "File to be classified" - }, - "output": { - "description": "Places the file into a folder (bin) that its classified into." - }, - "result": { - "type": "JSON", - "description": "JSON response containing the bin to which the file was classified into", - "schema": {} - }, - "adapter": { - "languageModels": [ - { - "isEnabled": true, - "title": "Classifier LLM", - "isRequired": true, - "description": "LLM to use for classification" - } - ], - "embeddingServices": [ - { - "isEnabled": false - } - ], - "vectorStores": [ - { - "isEnabled": false - } - ], - "textExtractors": [ - { - "isEnabled": true, - "adapterId": "textExtractorId", - "title": "Text Extraction Adapter", - "isRequired": true, - "description": "Choose extractor adapter to extract text" - } - ] - }, - "ioCompatibility": { - "api": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": { - "sync": true - } - }, - "file": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": {} - }, - "db": { - "destinationSupport": true, - "additionalArgs": {} - } - }, - "restrictions": { - "maxFileSize": "200MB", - "allowedFileTypes": [ - "*" - ] - } -} diff --git a/tools/classifier/src/config/runtime_variables.json b/tools/classifier/src/config/runtime_variables.json deleted file mode 100644 index 3f72970c3e..0000000000 --- a/tools/classifier/src/config/runtime_variables.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Runtime Variables", - "description": "Runtime Variables for classifier", - "type": "object", - "required": [], - "properties": {} -} diff --git a/tools/classifier/src/config/spec.json b/tools/classifier/src/config/spec.json deleted file mode 100644 index 5b5b59d14a..0000000000 --- a/tools/classifier/src/config/spec.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "title": "Document classifier tool settings", - "description": "Classify documents based on their content", - "type": "object", - "required": [ - "classificationBins" - ], - "properties": { - "classificationBins": { - "type": "array", - "title": "Classification bins", - "description": "Specify at least two unique classification bins. `unknown` and `__unstract_failed` are reserved bins.\n - `unknown` indicates the LLM can't determine the classification\n - `__unstract_failed` indicates a tool run failure for the given file.", - "items": { - "type": "string" - }, - "minItems": 2, - "uniqueItems": true - }, - "useCache": { - "type": "boolean", - "title": "Cache and use cached results", - "default": true, - "description": "Use cached results" - } - } -} diff --git a/tools/classifier/src/helper.py b/tools/classifier/src/helper.py deleted file mode 100644 index fe2c6eeab5..0000000000 --- a/tools/classifier/src/helper.py +++ /dev/null @@ -1,261 +0,0 @@ -import re -from pathlib import Path -from typing import Any - -from unstract.sdk1.adapters.x2text.dto import TextExtractionResult -from unstract.sdk1.constants import LogLevel, MetadataKey, UsageKwargs -from unstract.sdk1.llm import LLM -from unstract.sdk1.tool.base import BaseTool -from unstract.sdk1.x2txt import X2Text - - -class ReservedBins: - UNKNOWN = "unknown" - FAILED = "__unstract_failed" - - -class ClassifierHelper: - """Helper functions for Classifier.""" - - def __init__(self, tool: BaseTool, output_dir: str) -> None: - """Creates a helper class for the Classifier tool. - - Args: - tool (BaseTool): Base tool instance - output_dir (str): Output directory in EXECUTION_DATA_DIR - """ - self.tool = tool - self.output_dir = output_dir - - def stream_error_and_exit( - self, message: str, bin_to_copy_to: str = ReservedBins.FAILED - ) -> None: - """Streams error logs and performs required cleanup. - - Helper which copies files to a reserved bin in case of an error. - - Args: - message (str): Error message to log - bin_to_copy_to (str): The folder to copy the failed source file to. - Defaults to `__unstract_failed`. - input_file (Optional[str], optional): Input file to copy. Defaults to None. - output_dir (Optional[str], optional): Output directory to copy to. - Defaults to None. - """ - source_name = self.tool.get_exec_metadata.get(MetadataKey.SOURCE_NAME) - self.copy_source_to_output_bin( - classification=bin_to_copy_to, - source_file=self.tool.get_source_file(), - source_name=source_name, - ) - - self.tool.stream_error_and_exit(message=message) - - def copy_source_to_output_bin( - self, - classification: str, - source_file: str, - source_name: str, - ) -> None: - """Method to save result in output folder and the data directory. - - Args: - classification (str): classification result - source_file (str): Path to source file used in the workflow - source_name (str): Name of the actual input from the source - """ - try: - output_folder_bin = Path(self.output_dir) / classification - output_file = output_folder_bin / source_name - self._copy_file( - source_fs=self.tool.workflow_filestorage, - destination_fs=self.tool.workflow_filestorage, - source_path=source_file, - destination_path=str(output_file), - ) - except Exception as e: - self.tool.stream_error_and_exit(f"Error creating output file: {e}") - - def _copy_file( - self, - source_fs: Any, - destination_fs: Any, - source_path: str, - destination_path: str, - ) -> None: - """Helps copy a file from source to destination. - - Args: - src (str): Path to the source file - dest (str): Path to the destination file - """ - try: - # TODO: Move it to the top once SDK released with fileStorage Feature - # Change the source fs and destination fs type to to FileStorage - from unstract.sdk1.utils import FileStorageUtils - - FileStorageUtils.copy_file_to_destination( - source_storage=source_fs, - destination_storage=destination_fs, - source_path=source_path, - destination_paths=[destination_path], - ) - except Exception as e: - self.stream_error_and_exit(f"Error copying file: {e}") - - def extract_text( - self, file: str, text_extraction_adapter_id: str | None - ) -> str | None: - """Extract text from file. - - Args: - file (str): The path to the input file - - Returns: - str: page content - """ - if not text_extraction_adapter_id: - return self._extract_from_file(file) - - return self._extract_from_adapter(file, text_extraction_adapter_id) - - def _extract_from_adapter(self, file: str, adapter_id: str) -> str | None: - """Extract text from adapter. - - Args: - file: The path to the input file - adapter_id: The id of the adapter - Returns: - str: page content - """ - self.tool.stream_log( - f"Creating text extraction adapter using adapter_id: {adapter_id}" - ) - usage_kwargs: dict[Any, Any] = dict() - usage_kwargs[UsageKwargs.FILE_NAME] = self.tool.source_file_name - usage_kwargs[UsageKwargs.RUN_ID] = self.tool.file_execution_id - - x2text = X2Text( - tool=self.tool, adapter_instance_id=adapter_id, usage_kwargs=usage_kwargs - ) - - self.tool.stream_log("Text extraction adapter has been created successfully.") - - try: - extraction_result: TextExtractionResult = x2text.process( - input_file_path=file, - fs=self.tool.workflow_filestorage, - tags=self.tool.tags, - ) - extracted_text: str = extraction_result.extracted_text - return extracted_text - except Exception as e: - self.tool.stream_log(f"Adapter error: {e}") - return None - - def _extract_from_file(self, file: str) -> str | None: - """Extract text from file. - - Args: - file: The path to the input file - Returns: - str: page content - """ - self.tool.stream_log("Extracting text from file") - try: - text = self.tool.workflow_filestorage.read(path=file, mode="rb").decode( - "utf-8" - ) - except Exception as e: - self.tool.stream_log(f"File error: {e}") - return None - - self.tool.stream_log("Text extracted from file") - return text - - def find_classification( - self, - use_cache: bool, - settings_string: str, - bins: list[str], - prompt: str, - llm: LLM, - ) -> str | None: - """Find classification for text. - - Args: - use_cache (bool): Whether to use cache (deprecated, not used) - settings_string (str): hash of settings (deprecated, not used) - prompt (str): Prompt - bins (list[str]): Classification Bins - llm (LLM): LLM - - Returns: - Optional[str]: Classification from the LLM. - """ - self.tool.stream_log("Calling LLM for classification.") - llm_response = self.call_llm(prompt=prompt, llm=llm) - classification = self.clean_llm_response(llm_response=llm_response, bins=bins) - return classification - - def call_llm(self, prompt: str, llm: LLM) -> str: - """Call LLM. - - Args: - prompt (str): Prompt - llm (LLM): LLM - - Returns: - str: Classification - """ - try: - completion = llm.complete(prompt)[LLM.RESPONSE] - classification: str = completion.text.strip() - self.tool.stream_log(f"LLM response: {completion}", level=LogLevel.DEBUG) - return classification - except Exception as e: - self.stream_error_and_exit(f"Error calling LLM: {e}") - raise e - - def clean_llm_response(self, llm_response: str, bins: list[str]) -> str: - """Cleans the response from the LLM. - - Performs a substring search to find the returned classification. - Treats it as `unknown` if the classification is not clear - from the output. - - Args: - llm_response (str): Response from LLM to clean - bins (list(str)): List of bins to classify the file into. - - Returns: - str: Cleaned classification that matches one of the bins. - """ - classification = ReservedBins.UNKNOWN - cleaned_response = llm_response.strip().lower() - bins = [bin.lower() for bin in bins] - - # Truncate llm_response to the first 100 words - words = cleaned_response.split() - truncated_response = " ".join(words[:100]) - - # Count occurrences of each bin in the truncated text - bin_counts = { - bin: len(re.findall(r"\b" + re.escape(bin) + r"\b", truncated_response)) - for bin in bins - } - - # Filter bins that have a count greater than 0 - matching_bins = [bin for bin, count in bin_counts.items() if count > 0] - - # Determine classification based on the number of matching bins - if len(matching_bins) == 1: - classification = matching_bins[0] - else: - self.stream_error_and_exit( - f"Unable to deduce classified bin from possible values of " - f"'{matching_bins}', moving file to '{ReservedBins.UNKNOWN}' " - "folder instead.", - bin_to_copy_to=ReservedBins.UNKNOWN, - ) - return classification diff --git a/tools/classifier/src/main.py b/tools/classifier/src/main.py deleted file mode 100644 index 4903d727b2..0000000000 --- a/tools/classifier/src/main.py +++ /dev/null @@ -1,162 +0,0 @@ -import sys -from typing import Any - -from helper import ( - ClassifierHelper, # type: ignore - ReservedBins, -) - -from unstract.sdk1.constants import ( - LogLevel, - LogState, - MetadataKey, - ToolSettingsKey, - UsageKwargs, -) -from unstract.sdk1.exceptions import SdkError -from unstract.sdk1.llm import LLM -from unstract.sdk1.tool.base import BaseTool -from unstract.sdk1.tool.entrypoint import ToolEntrypoint - - -class UnstractClassifier(BaseTool): - def __init__(self, log_level: str = LogLevel.INFO) -> None: - super().__init__(log_level) - - def validate(self, input_file: str, settings: dict[str, Any]) -> None: - bins: list[str] | None = settings.get("classificationBins") - llm_adapter_instance_id = settings.get(ToolSettingsKey.LLM_ADAPTER_ID) - text_extraction_adapter_id = settings.get("textExtractorId") - if not bins: - self.stream_error_and_exit("Classification bins are required.") - elif len(bins) < 2: - self.stream_error_and_exit("At least two classification bins are required.") - elif ReservedBins.UNKNOWN in bins: - self.stream_error_and_exit( - f"Classification bin '{ReservedBins.UNKNOWN}' is reserved to mark " - "files which cannot be classified." - ) - - if not llm_adapter_instance_id: - self.stream_error_and_exit("Choose an LLM to perform the classification.") - if not text_extraction_adapter_id: - self.stream_error_and_exit( - "Choose a text extractor to extract the documents." - ) - - def run( - self, - settings: dict[str, Any], - input_file: str, - output_dir: str, - ) -> None: - bins = settings["classificationBins"] - use_cache = settings["useCache"] - text_extraction_adapter_id = settings["textExtractorId"] - llm_adapter_instance_id = settings[ToolSettingsKey.LLM_ADAPTER_ID] - self.helper = ClassifierHelper(tool=self, output_dir=output_dir) - - # Update GUI - input_log = f"### Classification bins:\n```text\n{bins}\n```\n\n" - output_log = "" - self.stream_update(input_log, state=LogState.INPUT_UPDATE) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - self.stream_log(f"Reading file... {input_file}") - text: str | None = self.helper.extract_text( - file=input_file, - text_extraction_adapter_id=text_extraction_adapter_id, - ) - if not text: - self.helper.stream_error_and_exit("Unable to extract text") - return - self.stream_log(f"Text length: {len(text)}") - - # Update GUI - input_text_for_log = text - if len(input_text_for_log) > 500: - input_text_for_log = input_text_for_log[:500] + "...(truncated)" - input_log = ( - f"### Classification bins:\n```text\n{bins}\n```\n\n" - f"### Input text:\n\n```text\n{input_text_for_log}\n```\n\n" - ) - output_log = "" - self.stream_update(input_log, state=LogState.INPUT_UPDATE) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - if ReservedBins.UNKNOWN not in bins: - bins.append(ReservedBins.UNKNOWN) - bins_with_quotes = [f"'{b}'" for b in bins] - - usage_kwargs: dict[Any, Any] = dict() - usage_kwargs[UsageKwargs.WORKFLOW_ID] = self.workflow_id - usage_kwargs[UsageKwargs.EXECUTION_ID] = self.execution_id - usage_kwargs[UsageKwargs.FILE_NAME] = self.source_file_name - usage_kwargs[UsageKwargs.RUN_ID] = self.file_execution_id - - try: - llm = LLM( - adapter_instance_id=llm_adapter_instance_id, - tool=self, - kwargs=usage_kwargs, - ) - - max_tokens = llm.get_max_tokens( - adapter_instance_id=llm_adapter_instance_id, - tool=self, - reserved_for_output=50 + 1000, - ) - except SdkError: - self.helper.stream_error_and_exit("Unable to get llm instance") - return - - max_bytes = int(max_tokens * 1.3) - self.stream_log(f"LLM Max tokens: {max_tokens} ==> Max bytes: {max_bytes}") - limited_text = "" - for byte in text.encode(): - if len(limited_text.encode()) < max_bytes: - limited_text += chr(byte) - else: - break - text = limited_text - self.stream_log(f"Length of text: {len(text.encode())} {len(text)}") - - prompt = ( - f"Classify the following text into one of the following categories: {' '.join(bins_with_quotes)}.\n\n" # noqa: E501 - "Your categorization should be strictly exactly one of the items in the " # noqa: E501 - "categories given, do not provide any explanation. Find a semantic match of category if possible. " # noqa: E501 - "If it does not categorize well into any of the listed categories, categorize it as 'unknown'." # noqa: E501 - f"Do not enclose the result within single quotes.\n\nText:\n\n{text}\n\n\nCategory:" # noqa: E501 - ) - - settings_string = "".join(str(value) for value in settings.values()) - classification = self.helper.find_classification( - use_cache=use_cache, - settings_string=settings_string, - prompt=prompt, - bins=bins, - llm=llm, - ) - - source_name = self.get_exec_metadata.get(MetadataKey.SOURCE_NAME) - self.helper.copy_source_to_output_bin( - classification=classification, - source_file=self.get_source_file(), - source_name=source_name, - ) - output_log = "### Classifier output\n\n" - output_log += f"```bash\nCLASSIFICATION={classification}\n```\n\n" - self.stream_single_step_message(output_log) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - classification_dict = { - "input_file": source_name, - "result": classification, - } - self.write_tool_result(data=classification_dict) - - -if __name__ == "__main__": - args = sys.argv[1:] - tool = UnstractClassifier.from_tool_args(args=args) - ToolEntrypoint.launch(tool=tool, args=args) diff --git a/tools/structure/.dockerignore b/tools/structure/.dockerignore deleted file mode 100644 index c26352afcc..0000000000 --- a/tools/structure/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -venv/ -.venv/ -.env diff --git a/tools/structure/.gitignore b/tools/structure/.gitignore deleted file mode 100644 index be0e458d46..0000000000 --- a/tools/structure/.gitignore +++ /dev/null @@ -1 +0,0 @@ -helper.py diff --git a/tools/structure/Dockerfile b/tools/structure/Dockerfile deleted file mode 100644 index c231636cc7..0000000000 --- a/tools/structure/Dockerfile +++ /dev/null @@ -1,68 +0,0 @@ -FROM python:3.12-slim-trixie - -LABEL maintainer="Zipstack Inc." \ - description="Structure Tool Container" \ - version="1.0" - -ENV \ - # Keeps Python from generating .pyc files in the container - PYTHONDONTWRITEBYTECODE=1 \ - # Set to immediately flush stdout and stderr streams without first buffering - PYTHONUNBUFFERED=1 \ - APP_HOME=/app \ - BUILD_PACKAGES_PATH=unstract \ - # OpenTelemetry configuration - OTEL_SERVICE_NAME="structure-tool" \ - OTEL_TRACES_EXPORTER=none \ - OTEL_METRICS_EXPORTER=none \ - OTEL_LOGS_EXPORTER=none \ - # Enable context propagation - OTEL_PROPAGATORS="tracecontext" \ - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python \ - # Increase timeout for large packages (flipt-client is ~45MB) - UV_HTTP_TIMEOUT=120 - -# Install system dependencies in one layer -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - ffmpeg git libsm6 libxext6 libmagic-dev poppler-utils \ - libreoffice freetds-dev freetds-bin dumb-init && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* - -# Install uv package manager -COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /uvx /bin/ - -# Set the working directory in the container -WORKDIR ${APP_HOME} - -# Copy only requirements file first for better caching -COPY tools/structure/requirements.txt ${APP_HOME}/ - -# Copy specific subdirectories while preserving structure -COPY ${BUILD_PACKAGES_PATH}/sdk1 /unstract/sdk1 -COPY ${BUILD_PACKAGES_PATH}/core /unstract/core -COPY ${BUILD_PACKAGES_PATH}/flags /unstract/flags - -# Install OpenTelemetry packages first (less likely to change) -RUN uv pip install --system \ - opentelemetry-distro \ - opentelemetry-exporter-otlp \ - platformdirs>=3.0.0 - -# Set shell options for better error handling -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# Install project dependencies separately from OpenTelemetry -# This allows for better caching when only project dependencies change -RUN uv pip install --system -r requirements.txt && \ - opentelemetry-bootstrap -a requirements | uv pip install --system --requirement - && \ - pip uninstall -y opentelemetry-instrumentation-openai-v2 && \ - pip install opentelemetry-instrumentation-openai - -# Copy the source code after installing all dependencies -# This ensures that changes to the source code don't invalidate the dependency layers -COPY tools/structure/src ${APP_HOME}/src/ -WORKDIR ${APP_HOME}/src - -ENTRYPOINT ["opentelemetry-instrument", "python", "main.py"] diff --git a/tools/structure/README.md b/tools/structure/README.md deleted file mode 100644 index 458f55895c..0000000000 --- a/tools/structure/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# Structure Tool - -This is a helper tool that needs to be used along with `Prompt Studio`. It helps process input files based on the prompts that have been designed and exported as a tool from `Prompt Studio`. - -## Required environment variables - -| Variable | Description | -| -------------------------- |-----------------------------------------------------------------------| -| `PLATFORM_SERVICE_HOST` | The host in which the platform service is running | -| `PLATFORM_SERVICE_PORT` | The port in which the service is listening | -| `PLATFORM_SERVICE_API_KEY` | The API key for the platform | -| `EXECUTION_DATA_DIR` | The directory in the filesystem which has contents for tool execution | -| `PROMPT_HOST` | The host in which the prompt service is running | -| `PROMPT_PORT` | The port in which the prompt service is listening | -| `PROMPT_PORT` | The port in which the prompt service is listening | -| `X2TEXT_HOST` | The host where the x2text service is running | -| `X2TEXT_PORT` | The port where the x2text service is listening | - -## Testing the tool locally - -### Setting up a dev environment - -Setup a virtual environment and activate it - -```commandline -python -m venv .venv -source .venv/bin/activate -``` - -Install the dependencies for the tool - -```commandline -pip install -r requirements.txt -``` - -To use the local development version of the [unstract-sdk](https://pypi.org/project/unstract-sdk/) install it from the local repository. -Replace the path with the path to your local repository - -```commandline -pip install -e ~/path_to_repo/sdks/. -``` - -### Tool execution preparation - -Load the environment variables for the tool. -Make a copy of the `sample.env` file and name it `.env`. Fill in the required values. -They get loaded with [python-dotenv](https://pypi.org/project/python-dotenv/) through the SDK. - -Update the tool's `data_dir` marked by the `EXECUTION_DATA_DIR` env. This has to be done before each tool execution since the tool updates the `INFILE` and `METADATA.json`. - -### Run SPEC command - -Represents the JSON schema for the runtime configurable `settings` of a tool - -```commandline -python main.py --command SPEC -``` - -### Run PROPERTIES command - -Describes some metadata for the tool such as its `version`, `description`, `inputs` and `outputs` - -```commandline -python main.py --command PROPERTIES -``` - -### Run ICON command - -Returns the SVG icon for the tool, used by Unstract's frontend - -```commandline -python main.py --command ICON -``` - -### Run VARIABLES command - -Represents the runtime variables or envs that will be used by the tool - -```commandline -python main.py --command VARIABLES -``` - -### Run RUN command - -The schema of the JSON required for settings can be found by running the [SPEC](#run-spec-command) command. Alternatively if you have access to the code base, it is located in the `config` folder as `spec.json`. - -```commandline -python main.py \ - --command RUN \ - --settings '{ - "prompt_registry_id": "" - }' \ - --log-level DEBUG - -``` - -## Testing the tool from its docker image - -Build the tool docker image from the folder containing the `Dockerfile` with - -```commandline -docker build -t unstract/tool-structure:0.0.1 . -``` - -Make sure the directory pointed by `EXECUTION_DATA_DIR` has the required information for the tool to run and -necessary services like the `platform-service` is up. -To test the tool from its docker image, run the following command - -```commandline -docker run -it \ - --network unstract-network \ - --env-file .env \ - -v "$(pwd)"/data_dir:/app/data_dir \ - unstract/tool-structure:0.0.1 \ - --command RUN \ - --settings '{ - "prompt_registry_id": "" - }' \ - --log-level DEBUG - -``` diff --git a/tools/structure/__init__.py b/tools/structure/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/structure/requirements.txt b/tools/structure/requirements.txt deleted file mode 100644 index aba611c0e5..0000000000 --- a/tools/structure/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Add your dependencies here - -# Required for all unstract tools -# aws alone is needed here -# because tools use transient temporary storage. --e file:/unstract/core --e file:/unstract/sdk1[aws] --e file:/unstract/flags -json-repair>=0.25.0 diff --git a/tools/structure/sample.env b/tools/structure/sample.env deleted file mode 100644 index 0e5f82c242..0000000000 --- a/tools/structure/sample.env +++ /dev/null @@ -1,16 +0,0 @@ -PLATFORM_SERVICE_HOST=http://unstract-platform-service -PLATFORM_SERVICE_PORT=3001 -PLATFORM_SERVICE_API_KEY= -EXECUTION_DATA_DIR=../data_dir -PROMPT_HOST=http://unstract-prompt-service -PROMPT_PORT=3003 - -X2TEXT_HOST=http://unstract-x2text-service -X2TEXT_PORT=3004 - -# File System Configuration for Workflow Execution -# Directory path for execution data storage -# (e.g., bucket/execution/org_id/workflow_id/execution_id) -EXECUTION_DATA_DIR= -# Storage provider for Workflow Execution (e.g., minio, S3) -WORKFLOW_EXECUTION_FILE_STORAGE_CREDENTIALS='{"provider":"minio","credentials"={"endpoint_url":"http://localhost:9000","key":"","secret":""}}' diff --git a/tools/structure/src/config/icon.svg b/tools/structure/src/config/icon.svg deleted file mode 100644 index e3a507bafa..0000000000 --- a/tools/structure/src/config/icon.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tools/structure/src/config/properties.json b/tools/structure/src/config/properties.json deleted file mode 100644 index 8dce918746..0000000000 --- a/tools/structure/src/config/properties.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "schemaVersion": "0.0.1", - "displayName": "Structure Tool", - "functionName": "structure_tool", - "toolVersion": "0.0.100", - "description": "This is a template tool which can answer set of input prompts designed in the Prompt Studio", - "input": { - "description": "File that needs to be indexed and parsed for answers" - }, - "output": { - "description": "A JSON file containing the parsed answers for the prompts" - }, - "result": { - "type": "JSON", - "description": "A JSON containing the parsed answers for the prompts", - "schema": {} - }, - "ioCompatibility": { - "api": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": { - "sync": true - } - }, - "file": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": {} - }, - "db": { - "destinationSupport": true, - "additionalArgs": {} - } - }, - "restrictions": { - "maxFileSize": "200MB", - "allowedFileTypes": [ - "*" - ] - } -} diff --git a/tools/structure/src/config/runtime_variables.json b/tools/structure/src/config/runtime_variables.json deleted file mode 100644 index 3fc0936224..0000000000 --- a/tools/structure/src/config/runtime_variables.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Runtime Variables", - "description": "Runtime Variables for Structure Tool ", - "type": "object" -} diff --git a/tools/structure/src/config/spec.json b/tools/structure/src/config/spec.json deleted file mode 100644 index cd73e39ec5..0000000000 --- a/tools/structure/src/config/spec.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "title": "Structure Tool", - "description": "Helper tool used to export tools from Prompt Studio", - "type": "object", - "required": [ - ], - "properties": { - } -} diff --git a/tools/structure/src/constants.py b/tools/structure/src/constants.py deleted file mode 100644 index 8da6a5701a..0000000000 --- a/tools/structure/src/constants.py +++ /dev/null @@ -1,108 +0,0 @@ -class SettingsKeys: - TOOL_INSTANCE_ID = "tool_instance_id" - PROMPT_REGISTRY_ID = "prompt_registry_id" - PROMPT_HOST = "PROMPT_HOST" - PROMPT_PORT = "PROMPT_PORT" - TOOL_METADATA = "tool_metadata" - TOOL_ID = "tool_id" - OUTPUTS = "outputs" - NAME = "name" - ACTIVE = "active" - PROMPT = "prompt" - CHUNK_SIZE = "chunk-size" - VECTOR_DB = "vector-db" - EMBEDDING = "embedding" - X2TEXT_ADAPTER = "x2text_adapter" - CHUNK_OVERLAP = "chunk-overlap" - LLM = "llm" - RETRIEVAL_STRATEGY = "retrieval-strategy" - SIMPLE = "simple" - TYPE = "type" - NUMBER = "number" - EMAIL = "email" - DATE = "date" - BOOLEAN = "boolean" - JSON = "json" - PREAMBLE = "preamble" - SIMILARITY_TOP_K = "similarity-top-k" - PROMPT_TOKENS = "prompt_tokens" - COMPLETION_TOKENS = "completion_tokens" - TOTAL_TOKENS = "total_tokens" - RESPONSE = "response" - POSTAMBLE = "postamble" - GRAMMAR = "grammar" - WORD = "word" - SYNONYMS = "synonyms" - OUTPUTS = "outputs" - SECTION = "section" - DEFAULT = "default" - AUTHOR = "author" - ICON = "icon" - TOOL_ID = "tool_id" - # PDF_TO_TEXT_CONVERTER = "pdf-to-text-converters" - REINDEX = "reindex" - STRUCTURE_OUTPUT = "structure_output" - TOOL_SETTINGS = "tool_settings" - ENABLE_SINGLE_PASS_EXTRACTION = "enable_single_pass_extraction" - CHALLENGE_LLM = "challenge_llm" - ENABLE_CHALLENGE = "enable_challenge" - SINGLE_PASS_EXTRACTION_MODE = "single_pass_extraction_mode" - CHALLENGE_LLM_ADAPTER_ID = "challenge_llm_adapter_id" - SUMMARIZE_AS_SOURCE = "summarize_as_source" - SUMMARIZE_PROMPT = "summarize_prompt" - CONTEXT = "context" - ERROR = "error" - LLM_ADAPTER_INSTANCE_ID = "llm_adapter_instance_id" - RUN_ID = "run_id" - PROMPT_KEYS = "prompt_keys" - DATA = "data" - EXTRACT = "EXTRACT" - SUMMARIZE = "SUMMARIZE" - STATUS = "status" - OK = "OK" - FILE_NAME = "file_name" - FILE_HASH = "file_hash" - ENABLE_HIGHLIGHT = "enable_highlight" - ENABLE_WORD_CONFIDENCE = "enable_word_confidence" - NAME = "name" - INCLUDE_METADATA = "include_metadata" - TABLE_SETTINGS = "table_settings" - INPUT_FILE = "input_file" - METADATA = "metadata" - EPILOGUE = "epilogue" - HIGHLIGHT_DATA = "highlight_data" - CONFIDENCE_DATA = "confidence_data" - FILE_PATH = "file_path" - EXECUTION_SOURCE = "execution_source" - TOOL = "tool" - METRICS = "metrics" - INDEXING = "indexing" - EXECUTION_ID = "execution_id" - IS_DIRECTORY_MODE = "is_directory_mode" - LLM_PROFILE_ID = "llm_profile_id" - CUSTOM_DATA = "custom_data" - OUTPUT = "output" # For API deployment response format compatibility - - -class IndexingConstants: - TOOL_ID = "tool_id" - EMBEDDING_INSTANCE_ID = "embedding_instance_id" - VECTOR_DB_INSTANCE_ID = "vector_db_instance_id" - X2TEXT_INSTANCE_ID = "x2text_instance_id" - FILE_PATH = "file_path" - CHUNK_SIZE = "chunk_size" - CHUNK_OVERLAP = "chunk_overlap" - REINDEX = "reindex" - FILE_HASH = "file_hash" - OUTPUT_FILE_PATH = "output_file_path" - ENABLE_HIGHLIGHT = "enable_highlight" - USAGE_KWARGS = "usage_kwargs" - PROCESS_TEXT = "process_text" - EXTRACTED_TEXT = "extracted_text" - TAGS = "tags" - EXECUTION_SOURCE = "execution_source" - DOC_ID = "doc_id" - TOOL_EXECUTION_METATADA = "tool_execution_metadata" - EXECUTION_DATA_DIR = "execution_data_dir" - RUN_ID = "run_id" - EXECUTION_ID = "execution_id" diff --git a/tools/structure/src/helpers.py b/tools/structure/src/helpers.py deleted file mode 100644 index 2cb0d9bf72..0000000000 --- a/tools/structure/src/helpers.py +++ /dev/null @@ -1,134 +0,0 @@ -import datetime -import logging -from collections.abc import Callable -from typing import Any - -from constants import IndexingConstants as IKeys -from constants import SettingsKeys # type: ignore [attr-defined] - -from unstract.sdk1.prompt import PromptTool -from unstract.sdk1.tool.base import BaseTool - -logger = logging.getLogger(__name__) - - -class StructureToolHelper: - @staticmethod - def dynamic_extraction( - file_path: str, - enable_highlight: bool, - usage_kwargs: dict[str, Any], - run_id: str, - tool_settings: dict[str, Any], - extract_file_path: str, - tool: BaseTool, - execution_run_data_folder: str, - ) -> str: - x2text = tool_settings[SettingsKeys.X2TEXT_ADAPTER] - payload = { - IKeys.X2TEXT_INSTANCE_ID: x2text, - IKeys.FILE_PATH: file_path, - IKeys.ENABLE_HIGHLIGHT: enable_highlight, - IKeys.USAGE_KWARGS: usage_kwargs.copy(), - IKeys.RUN_ID: run_id, - IKeys.EXECUTION_SOURCE: SettingsKeys.TOOL, - IKeys.OUTPUT_FILE_PATH: str(extract_file_path), - IKeys.TAGS: tool.tags, - IKeys.TOOL_EXECUTION_METATADA: tool.get_exec_metadata, - IKeys.EXECUTION_DATA_DIR: str(execution_run_data_folder), - } - - logger.info(f"Prompt service payload for text extraction:\n{payload}") - - prompt_tool = PromptTool( - tool=tool, - prompt_host=tool.get_env_or_die(SettingsKeys.PROMPT_HOST), - prompt_port=tool.get_env_or_die(SettingsKeys.PROMPT_PORT), - request_id=run_id, - ) - return prompt_tool.extract(payload=payload) - - @staticmethod - def dynamic_indexing( - file_path: str, - tool_settings: dict[str, Any], - run_id: str, - tool: BaseTool, - execution_run_data_folder: str, - reindex: bool, - usage_kwargs: dict[str, Any], - enable_highlight: bool, - chunk_size: int, - chunk_overlap: int, - file_hash: str | None = None, - tool_id: str = None, - extracted_text: str = None, - ) -> str: - x2text = tool_settings[SettingsKeys.X2TEXT_ADAPTER] - - payload = { - IKeys.TOOL_ID: tool_id, - IKeys.EMBEDDING_INSTANCE_ID: tool_settings[SettingsKeys.EMBEDDING], - IKeys.VECTOR_DB_INSTANCE_ID: tool_settings[SettingsKeys.VECTOR_DB], - IKeys.X2TEXT_INSTANCE_ID: x2text, - IKeys.FILE_HASH: file_hash, - IKeys.CHUNK_SIZE: chunk_size, - IKeys.CHUNK_OVERLAP: chunk_overlap, - IKeys.REINDEX: reindex, - IKeys.FILE_PATH: str(file_path), - IKeys.ENABLE_HIGHLIGHT: enable_highlight, - IKeys.USAGE_KWARGS: usage_kwargs.copy(), - IKeys.RUN_ID: run_id, - IKeys.EXECUTION_SOURCE: SettingsKeys.TOOL, - IKeys.TAGS: tool.tags, - IKeys.TOOL_EXECUTION_METATADA: tool.get_exec_metadata, - IKeys.EXECUTION_DATA_DIR: str(execution_run_data_folder), - IKeys.EXTRACTED_TEXT: extracted_text, - } - - sensitive_keys = [IKeys.EXTRACTED_TEXT] - payload_to_log = {k: v for k, v in payload.items() if k not in sensitive_keys} - logger.info(f"Prompt service payload for indexing:\n{payload_to_log}") - responder = PromptTool( - tool=tool, - prompt_host=tool.get_env_or_die(SettingsKeys.PROMPT_HOST), - prompt_port=tool.get_env_or_die(SettingsKeys.PROMPT_PORT), - request_id=run_id, - ) - return responder.index(payload=payload) - - @staticmethod - def handle_profile_overrides( - tool: BaseTool, - llm_profile_to_override: dict, - llm_profile_id: str, - tool_metadata: dict, - apply_profile_overrides_func: Callable[[dict, dict], list[str]], - ) -> None: - """Handle profile overrides and logging. - - Args: - tool: The tool instance for logging - llm_profile_to_override: The profile data to apply, or None if no profile - llm_profile_id: The profile ID for logging purposes - tool_metadata: The tool metadata dictionary to modify - apply_profile_overrides_func: Function to apply profile overrides - """ - if llm_profile_to_override: - tool.stream_log( - f"Applying profile overrides from profile: {llm_profile_to_override.get('profile_name', llm_profile_id)}" - ) - changes = apply_profile_overrides_func(tool_metadata, llm_profile_to_override) - if changes: - tool.stream_log("Profile overrides applied successfully. Changes made:") - for change in changes: - tool.stream_log(f" - {change}") - else: - tool.stream_log( - "Profile overrides applied - no changes needed (values already matched)" - ) - - @staticmethod - def elapsed_time(start_time) -> float: - """Returns the elapsed time since the process was started.""" - return (datetime.datetime.now() - start_time).total_seconds() diff --git a/tools/structure/src/main.py b/tools/structure/src/main.py deleted file mode 100644 index f68143a6c8..0000000000 --- a/tools/structure/src/main.py +++ /dev/null @@ -1,772 +0,0 @@ -import datetime -import json -import logging -import os -import sys -from pathlib import Path -from typing import Any - -from constants import SettingsKeys # type: ignore [attr-defined] -from helpers import StructureToolHelper as STHelper -from utils import json_to_markdown, repair_json_with_best_structure - -from unstract.sdk1.constants import LogState, MetadataKey, ToolEnv, UsageKwargs -from unstract.sdk1.platform import PlatformHelper -from unstract.sdk1.prompt import PromptTool -from unstract.sdk1.tool.base import BaseTool -from unstract.sdk1.tool.entrypoint import ToolEntrypoint - -logger = logging.getLogger(__name__) - -PAID_FEATURE_MSG = ( - "It is a cloud / enterprise feature. If you have purchased a plan and still " - "face this issue, please contact support" -) - - -class StructureTool(BaseTool): - def _apply_profile_overrides( - self, tool_metadata: dict, profile_data: dict - ) -> list[str]: - """Apply profile overrides to tool metadata. - - Args: - tool_metadata: The tool metadata dictionary to modify - profile_data: The profile data containing override values - - Returns: - List of change descriptions - """ - changes = [] - - # Mapping between profile keys and tool metadata keys - profile_to_tool_mapping = { - "chunk_overlap": "chunk-overlap", - "chunk_size": "chunk-size", - "embedding_model_id": "embedding", - "llm_id": "llm", - "similarity_top_k": "similarity-top-k", - "vector_store_id": "vector-db", - "x2text_id": "x2text_adapter", - "retrieval_strategy": "retrieval-strategy", - } - - # Override tool_settings section - if "tool_settings" in tool_metadata: - tool_changes = self._override_section( - tool_metadata["tool_settings"], - profile_data, - profile_to_tool_mapping, - section_name="tool_settings", - ) - changes.extend(tool_changes) - - # Override each output in outputs section - if "outputs" in tool_metadata: - for i, output in enumerate(tool_metadata["outputs"]): - output_name = output.get("name", f"output_{i}") - output_changes = self._override_section( - output, - profile_data, - profile_to_tool_mapping, - section_name=f"output[{output_name}]", - ) - changes.extend(output_changes) - - return changes - - def _override_section( - self, - section: dict, - profile_data: dict, - mapping: dict, - section_name: str = "section", - ) -> list[str]: - """Override values in a section using profile data. - - Args: - section: The section dictionary to modify - profile_data: The profile data containing override values - mapping: Mapping between profile keys and section keys - section_name: Name of the section for logging purposes - - Returns: - List of change descriptions - """ - changes = [] - for profile_key, section_key in mapping.items(): - if profile_key in profile_data and section_key in section: - old_value = section[section_key] - new_value = profile_data[profile_key] - if old_value != new_value: # Only track actual changes - section[section_key] = new_value - change_desc = ( - f"{section_name}.{section_key}: {old_value} -> {new_value}" - ) - changes.append(change_desc) - self.stream_log(f"Overrode {change_desc}") - return changes - - def _should_skip_extraction_for_smart_table( - self, input_file: str, outputs: list[dict[str, Any]] - ) -> bool: - """Check if extraction and indexing should be skipped for smart table extraction. - - Args: - input_file: Path to the input file - outputs: List of output configurations - - Returns: - True if extraction/indexing should be skipped, False otherwise - """ - # Check if any output has table_settings with valid JSON prompt - for output in outputs: - if SettingsKeys.TABLE_SETTINGS in output: - prompt = output.get(SettingsKeys.PROMPT, "") - if prompt and isinstance(prompt, str): - try: - # Try to parse the prompt as JSON - schema_data = repair_json_with_best_structure(prompt) - # If it's a valid dict (schema object), skip extraction - if schema_data and isinstance(schema_data, dict): - return True - except Exception as e: - logger.warning( - "Failed to parse prompt as JSON for smart table extraction: %s", - str(e), - ) - continue - return False - - def validate(self, input_file: str, settings: dict[str, Any]) -> None: - enable_challenge: bool = settings.get(SettingsKeys.ENABLE_CHALLENGE, False) - challenge_llm: str = settings.get(SettingsKeys.CHALLENGE_LLM_ADAPTER_ID, "") - if enable_challenge and not challenge_llm: - raise ValueError("Challenge LLM is not set after enabling Challenge") - - def _is_agentic_project(self, tool_metadata: dict[str, Any]) -> bool: - """Check if metadata indicates an agentic project. - - Agentic projects have project_id and json_schema fields. - Prompt studio projects have tool_id and outputs fields. - """ - return "project_id" in tool_metadata and "json_schema" in tool_metadata - - def run( - self, - settings: dict[str, Any], - input_file: str, - output_dir: str, - ) -> None: - prompt_registry_id: str = settings[SettingsKeys.PROMPT_REGISTRY_ID] - is_challenge_enabled: bool = settings.get(SettingsKeys.ENABLE_CHALLENGE, False) - is_summarization_enabled: bool = settings.get( - SettingsKeys.SUMMARIZE_AS_SOURCE, False - ) - is_single_pass_enabled: bool = settings.get( - SettingsKeys.SINGLE_PASS_EXTRACTION_MODE, False - ) - challenge_llm: str = settings.get(SettingsKeys.CHALLENGE_LLM_ADAPTER_ID, "") - is_highlight_enabled: bool = settings.get(SettingsKeys.ENABLE_HIGHLIGHT, False) - responder: PromptTool = PromptTool( - tool=self, - prompt_port=self.get_env_or_die(SettingsKeys.PROMPT_PORT), - prompt_host=self.get_env_or_die(SettingsKeys.PROMPT_HOST), - request_id=self.file_execution_id, - ) - self.stream_log(f"Fetching exported tool with UUID '{prompt_registry_id}'") - - platform_helper: PlatformHelper = PlatformHelper( - tool=self, - platform_port=self.get_env_or_die(ToolEnv.PLATFORM_PORT), - platform_host=self.get_env_or_die(ToolEnv.PLATFORM_HOST), - request_id=self.file_execution_id, - ) - - # Try to fetch as prompt studio tool first - tool_metadata = None - is_agentic = False - exported_tool = None - - try: - exported_tool = platform_helper.get_prompt_studio_tool( - prompt_registry_id=prompt_registry_id - ) - except Exception as e: - # If prompt studio lookup fails, try as agentic project - self.stream_log( - f"Not found as prompt studio project, trying agentic registry: {e}" - ) - - if exported_tool and SettingsKeys.TOOL_METADATA in exported_tool: - tool_metadata = exported_tool[SettingsKeys.TOOL_METADATA] - is_agentic = False - # Explicitly mark metadata for clarity - tool_metadata["is_agentic"] = False - else: - # Try agentic registry as fallback - try: - agentic_tool = platform_helper.get_agentic_studio_tool( - agentic_registry_id=prompt_registry_id - ) - if not agentic_tool or SettingsKeys.TOOL_METADATA not in agentic_tool: - self.stream_error_and_exit( - f"Error fetching project: Registry returned empty response for {prompt_registry_id}" - ) - tool_metadata = agentic_tool[SettingsKeys.TOOL_METADATA] - is_agentic = True - # Explicitly mark metadata for clarity - tool_metadata["is_agentic"] = True - self.stream_log( - f"Retrieved agentic project: {tool_metadata.get('name', prompt_registry_id)}" - ) - except Exception as agentic_error: - self.stream_error_and_exit( - f"Error fetching project from both registries " - f"for ID '{prompt_registry_id}': {agentic_error}" - ) - - # Route to appropriate extraction method - if is_agentic: - return self._run_agentic_extraction( - tool_metadata=tool_metadata, - input_file=input_file, - output_dir=output_dir, - settings=settings, - responder=responder, - platform_helper=platform_helper, - ) - - # Continue with regular prompt studio extraction - llm_profile_id = self.get_exec_metadata.get(SettingsKeys.LLM_PROFILE_ID) - llm_profile_to_override = None - try: - if llm_profile_id: - llm_profile = platform_helper.get_llm_profile(llm_profile_id) - llm_profile_to_override = llm_profile - - # Apply profile overrides if available - STHelper.handle_profile_overrides( - self, - llm_profile_to_override, - llm_profile_id, - tool_metadata, - self._apply_profile_overrides, - ) - except Exception as e: - self.stream_error_and_exit(f"Error fetching prompt studio project: {e}") - ps_project_name = tool_metadata.get("name", prompt_registry_id) - # Count only the active (enabled) prompts - total_prompt_count = len(tool_metadata[SettingsKeys.OUTPUTS]) - self.stream_log( - f"Retrieved prompt studio exported tool '{ps_project_name}' having " - f"'{total_prompt_count}' prompts" - ) - - active_prompt_count = len( - [ - output - for output in tool_metadata[SettingsKeys.OUTPUTS] - if output.get("active", False) - ] - ) - # Update GUI - input_log = f"## Loaded '{ps_project_name}'\n{json_to_markdown(tool_metadata)}\n" - output_log = ( - f"## Processing '{self.source_file_name}'\nThis might take a while and " - "involve...\n- Extracting text\n- Indexing\n- Retrieving answers " - f"for '{active_prompt_count}' prompts" - ) - self.stream_update(input_log, state=LogState.INPUT_UPDATE) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - file_hash = self.get_exec_metadata.get(MetadataKey.SOURCE_HASH) - tool_id = tool_metadata[SettingsKeys.TOOL_ID] - tool_settings = tool_metadata[SettingsKeys.TOOL_SETTINGS] - outputs = tool_metadata[SettingsKeys.OUTPUTS] - tool_settings[SettingsKeys.CHALLENGE_LLM] = challenge_llm - tool_settings[SettingsKeys.ENABLE_CHALLENGE] = is_challenge_enabled - tool_settings[SettingsKeys.ENABLE_SINGLE_PASS_EXTRACTION] = is_single_pass_enabled - tool_settings[SettingsKeys.SUMMARIZE_AS_SOURCE] = is_summarization_enabled - tool_settings[SettingsKeys.ENABLE_HIGHLIGHT] = is_highlight_enabled - _, file_name = os.path.split(input_file) - if is_summarization_enabled: - file_name = SettingsKeys.SUMMARIZE - tool_data_dir = Path(self.get_env_or_die(ToolEnv.EXECUTION_DATA_DIR)) - execution_run_data_folder = Path(self.get_env_or_die(ToolEnv.EXECUTION_DATA_DIR)) - - extracted_input_file = str(execution_run_data_folder / SettingsKeys.EXTRACT) - # Resolve and pass log events ID - payload = { - SettingsKeys.RUN_ID: self.file_execution_id, - SettingsKeys.EXECUTION_ID: self.execution_id, - SettingsKeys.TOOL_SETTINGS: tool_settings, - SettingsKeys.OUTPUTS: outputs, - SettingsKeys.TOOL_ID: tool_id, - SettingsKeys.FILE_HASH: file_hash, - SettingsKeys.FILE_NAME: file_name, - SettingsKeys.FILE_PATH: extracted_input_file, - SettingsKeys.EXECUTION_SOURCE: SettingsKeys.TOOL, - } - - custom_data = self.get_exec_metadata.get(SettingsKeys.CUSTOM_DATA, {}) - payload["custom_data"] = custom_data - - # Check if we should skip extraction and indexing for Excel table extraction with valid JSON - skip_extraction_and_indexing = self._should_skip_extraction_for_smart_table( - input_file, outputs - ) - - extracted_text = "" - usage_kwargs: dict[Any, Any] = dict() - if skip_extraction_and_indexing: - self.stream_log( - "Skipping extraction and indexing for Excel table with valid JSON schema" - ) - else: - self.stream_log(f"Extracting document '{self.source_file_name}'") - usage_kwargs[UsageKwargs.RUN_ID] = self.file_execution_id - usage_kwargs[UsageKwargs.FILE_NAME] = self.source_file_name - usage_kwargs[UsageKwargs.EXECUTION_ID] = self.execution_id - extracted_text = STHelper.dynamic_extraction( - file_path=input_file, - enable_highlight=is_highlight_enabled, - usage_kwargs=usage_kwargs, - run_id=self.file_execution_id, - tool_settings=tool_settings, - extract_file_path=tool_data_dir / SettingsKeys.EXTRACT, - tool=self, - execution_run_data_folder=str(execution_run_data_folder), - ) - - index_metrics = {} - if is_summarization_enabled: - summarize_file_path, summarize_file_hash = self._summarize( - tool_settings=tool_settings, - tool_data_dir=tool_data_dir, - responder=responder, - outputs=outputs, - usage_kwargs=usage_kwargs, - ) - payload[SettingsKeys.FILE_HASH] = summarize_file_hash - payload[SettingsKeys.FILE_PATH] = summarize_file_path - elif skip_extraction_and_indexing: - # Use source file directly for Excel with valid JSON - payload[SettingsKeys.FILE_PATH] = input_file - pass - elif not is_single_pass_enabled: - # Track seen parameter combinations to avoid duplicate indexing - seen_params = set() - - for output in outputs: - # Get current parameter combination - chunk_size = output[SettingsKeys.CHUNK_SIZE] - chunk_overlap = output[SettingsKeys.CHUNK_OVERLAP] - vector_db = tool_settings[SettingsKeys.VECTOR_DB] - embedding = tool_settings[SettingsKeys.EMBEDDING] - x2text = tool_settings[SettingsKeys.X2TEXT_ADAPTER] - - # Create a unique key for this parameter combination - param_key = ( - f"chunk_size={chunk_size}_" - f"chunk_overlap={chunk_overlap}_" - f"vector_db={vector_db}_" - f"embedding={embedding}_" - f"x2text={x2text}" - ) - - # Only process if we haven't seen this combination yet and chunk_size is not zero - if chunk_size != 0 and param_key not in seen_params: - seen_params.add(param_key) - - indexing_start_time = datetime.datetime.now() - self.stream_log( - f"Indexing document with: chunk_size={chunk_size}, " - f"chunk_overlap={chunk_overlap}, vector_db={vector_db}, " - f"embedding={embedding}, x2text={x2text}" - ) - - STHelper.dynamic_indexing( - tool_settings=tool_settings, - run_id=self.file_execution_id, - file_path=tool_data_dir / SettingsKeys.EXTRACT, - tool=self, - execution_run_data_folder=str(execution_run_data_folder), - chunk_overlap=chunk_overlap, - reindex=True, - usage_kwargs=usage_kwargs, - enable_highlight=is_highlight_enabled, - chunk_size=chunk_size, - tool_id=tool_metadata[SettingsKeys.TOOL_ID], - file_hash=file_hash, - extracted_text=extracted_text, - ) - - index_metrics[output[SettingsKeys.NAME]] = { - SettingsKeys.INDEXING: { - "time_taken(s)": STHelper.elapsed_time( - start_time=indexing_start_time - ) - } - } - - if is_single_pass_enabled: - self.stream_log("Fetching response for single pass extraction...") - structured_output = responder.single_pass_extraction( - payload=payload, - ) - else: - for output in outputs: - if SettingsKeys.TABLE_SETTINGS in output: - table_settings = output[SettingsKeys.TABLE_SETTINGS] - is_directory_mode: bool = table_settings.get( - SettingsKeys.IS_DIRECTORY_MODE, False - ) - # Use source file directly for Excel with valid JSON, otherwise use extracted file - if skip_extraction_and_indexing: - table_settings[SettingsKeys.INPUT_FILE] = input_file - payload[SettingsKeys.FILE_PATH] = input_file - else: - table_settings[SettingsKeys.INPUT_FILE] = extracted_input_file - table_settings[SettingsKeys.IS_DIRECTORY_MODE] = is_directory_mode - self.stream_log(f"Performing table extraction with: {table_settings}") - output.update({SettingsKeys.TABLE_SETTINGS: table_settings}) - - self.stream_log(f"Fetching responses for '{len(outputs)}' prompt(s)...") - structured_output = responder.answer_prompt( - payload=payload, - ) - - # HACK: Replacing actual file's name instead of INFILE - # Ensure metadata section exists - if SettingsKeys.METADATA not in structured_output: - structured_output[SettingsKeys.METADATA] = {} - self.stream_log("Created metadata section in structured_output") - - structured_output[SettingsKeys.METADATA][SettingsKeys.FILE_NAME] = ( - self.source_file_name - ) - - # Add extracted text for HITL raw view - if extracted_text: - structured_output[SettingsKeys.METADATA]["extracted_text"] = extracted_text - self.stream_log( - f"Added text extracted from the document to metadata (length: {len(extracted_text)} characters)" - ) - else: - self.stream_log( - "No text is extracted from the document to add to the metadata" - ) - if merged_metrics := self._merge_metrics( - structured_output.get(SettingsKeys.METRICS, {}), index_metrics - ): - structured_output[SettingsKeys.METRICS] = merged_metrics - # Update GUI - output_log = ( - f"## Result\n**NOTE:** In case of a deployed pipeline, the result would " - "be a JSON. This has been rendered for readability here\n" - f"{json_to_markdown(structured_output)}\n" - ) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - try: - self.stream_log( - "Writing prompt studio project's output to workflow's storage" - ) - output_path = Path(output_dir) / f"{Path(self.source_file_name).stem}.json" - self.workflow_filestorage.json_dump(path=output_path, data=structured_output) - self.stream_log( - "Prompt studio project's output written successfully to workflow's storage" - ) - except OSError as e: - self.stream_error_and_exit(f"Error creating output file: {e}") - except json.JSONDecodeError as e: - self.stream_error_and_exit(f"Error encoding JSON: {e}") - self.write_tool_result(data=structured_output) - - def _remove_source_refs(self, data: Any) -> Any: - """Recursively remove _source_refs from data structure. - - Args: - data: Data structure (dict, list, or primitive) to clean - - Returns: - Cleaned data structure without _source_refs fields - """ - if isinstance(data, dict): - return { - key: self._remove_source_refs(value) - for key, value in data.items() - if key != "_source_refs" - } - elif isinstance(data, list): - return [self._remove_source_refs(item) for item in data] - else: - return data - - def _merge_metrics(self, metrics1: dict, metrics2: dict) -> dict: - """Intelligently merge two metrics dictionaries. - - For keys that exist in both dictionaries with dictionary values, merge the dictionaries. - For keys that exist in only one dictionary or have non-dictionary values, use the value as-is. - - Args: - metrics1 (dict): First metrics dictionary - metrics2 (dict): Second metrics dictionary - - Returns: - dict: Merged metrics dictionary - """ - merged_metrics = {} - - # Get all unique keys from both dictionaries - all_keys = set(metrics1) | set(metrics2) - - for key in all_keys: - # If key exists in both dictionaries and both values are dictionaries, merge them - if ( - key in metrics1 - and key in metrics2 - and isinstance(metrics1[key], dict) - and isinstance(metrics2[key], dict) - ): - merged_metrics[key] = {**metrics1[key], **metrics2[key]} - # Otherwise just take the value from whichever dictionary has it - elif key in metrics1: - merged_metrics[key] = metrics1[key] - else: - merged_metrics[key] = metrics2[key] - - return merged_metrics - - def _run_agentic_extraction( - self, - tool_metadata: dict[str, Any], - input_file: str, - output_dir: str, - settings: dict[str, Any], - responder: PromptTool, - platform_helper: PlatformHelper, - ) -> None: - """Execute agentic extraction pipeline. - - Args: - tool_metadata: Complete agentic project metadata - input_file: Path to input document - output_dir: Directory for output files - settings: Workflow settings (contains adapter overrides) - responder: PromptTool instance for API calls - platform_helper: PlatformHelper instance for platform API calls - """ - from unstract.sdk1.x2txt import X2Text - - # Extract agentic-specific metadata - project_id = tool_metadata.get("project_id") - project_name = tool_metadata.get("name", project_id) - json_schema = tool_metadata.get("json_schema", {}) - prompt_text = tool_metadata.get("prompt_text", "") - prompt_version = tool_metadata.get("prompt_version", 1) - schema_version = tool_metadata.get("schema_version", 1) - adapter_config = tool_metadata.get("adapter_config", {}) - - self.stream_log( - f"Executing agentic extraction for project '{project_name}' " - f"(schema v{schema_version}, prompt v{prompt_version})" - ) - - # Get adapter IDs from settings (workflow UI overrides) - # or fall back to exported defaults - extractor_llm = settings.get( - "extractor_llm_adapter_id", adapter_config.get("extractor_llm") - ) - llmwhisperer = settings.get( - "llmwhisperer_adapter_id", adapter_config.get("llmwhisperer") - ) - enable_highlight = settings.get( - SettingsKeys.ENABLE_HIGHLIGHT, tool_metadata.get("enable_highlight", False) - ) - - # Get platform details for organization_id - platform_details = platform_helper.get_platform_details() - organization_id = ( - platform_details.get("organization_id") if platform_details else None - ) - - if not organization_id: - self.stream_error_and_exit("Failed to get organization_id from platform") - - # Update GUI - input_log = ( - f"## Loaded Agentic Project '{project_name}'\n" - f"- **Project ID**: {project_id}\n" - f"- **Schema Version**: {schema_version}\n" - f"- **Prompt Version**: {prompt_version}\n" - f"- **Extractor LLM**: {extractor_llm}\n" - ) - output_log = ( - f"## Processing '{self.source_file_name}'\n" - "Executing agentic extraction pipeline...\n" - "- Extracting document text\n" - "- Running LLM extraction\n" - ) - self.stream_update(input_log, state=LogState.INPUT_UPDATE) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - try: - # Step 1: Extract text from document using X2Text/LLMWhisperer - self.stream_log("Extracting text from document...") - x2text = X2Text(tool=self, adapter_instance_id=llmwhisperer) - - extraction_result = x2text.process( - input_file_path=input_file, - enable_highlight=enable_highlight, - fs=self.workflow_filestorage, - ) - - document_text = extraction_result.extracted_text - line_metadata = ( - extraction_result.extraction_metadata.line_metadata - if extraction_result.extraction_metadata - else None - ) - - self.stream_log(f"Extracted {len(document_text)} characters of text") - - # Step 2: Build extraction payload for /agentic/extract endpoint - payload = { - "document_id": self.file_execution_id, # Use run ID as document ID - "prompt_text": prompt_text, - "document_text": document_text, - "schema": json_schema, - "organization_id": organization_id, - "adapter_instance_id": extractor_llm, - "include_source_refs": enable_highlight, - } - - # Add line_metadata if available for highlighting - if line_metadata and enable_highlight: - payload["line_metadata"] = line_metadata - self.stream_log( - f"Including {len(line_metadata)} line metadata entries for highlighting" - ) - - # Step 3: Call agentic extraction endpoint - self.stream_log("Calling agentic extraction endpoint...") - extraction_response = responder.agentic_extraction(payload=payload) - - # Step 4: Process response from agentic extraction - extracted_data = extraction_response.get(SettingsKeys.OUTPUT, {}) - - # Remove _source_refs from extracted data - try: - extracted_data = self._remove_source_refs(extracted_data) - except Exception as e: - self.stream_log( - f"Warning: Failed to remove _source_refs: {e}. " - "Proceeding with original data." - ) - - # Build final structured output in prompt studio format - structured_output = { - SettingsKeys.OUTPUT: extracted_data, - SettingsKeys.METADATA: { - **extraction_response.get(SettingsKeys.METADATA, {}), - SettingsKeys.FILE_NAME: self.source_file_name, - "project_id": project_id, - "schema_version": schema_version, - "prompt_version": prompt_version, - "document_id": self.file_execution_id, - }, - } - output_log = ( - f"## Agentic Extraction Complete\n" - f"Successfully extracted data from '{self.source_file_name}'\n" - f"{json_to_markdown(structured_output)}\n" - ) - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - # Write output to file - self.stream_log("Writing agentic extraction output to workflow storage") - output_path = Path(output_dir) / f"{Path(self.source_file_name).stem}.json" - self.workflow_filestorage.json_dump(path=output_path, data=structured_output) - self.stream_log("Output written successfully to workflow storage") - - # Write tool result - self.write_tool_result(data=structured_output) - - except Exception as e: - self.stream_error_and_exit(f"Error during agentic extraction: {e}") - - def _summarize( - self, - tool_settings: dict[str, Any], - tool_data_dir: Path, - responder: PromptTool, - outputs: dict[str, Any], - usage_kwargs: dict[Any, Any] = {}, - ) -> tuple[str, str]: - """Summarizes the context of the file. - - Args: - tool_settings (dict[str, Any]): Settings for the tool. - tool_data_dir (Path): Directory where tool data is stored. - responder (PromptTool): Instance of a tool used to generate the summary. - outputs (dict[str, Any]): Dictionary containing prompt details. - usage_kwargs (dict[Any, Any]): Used to capture usage metrics. - - Returns: - tuple[str, str]: Tuple containing the path to the summarized file and its hash. - """ - llm_adapter_instance_id: str = tool_settings[SettingsKeys.LLM] - embedding_instance_id: str = tool_settings[SettingsKeys.EMBEDDING] - vector_db_instance_id: str = tool_settings[SettingsKeys.VECTOR_DB] - x2text_instance_id: str = tool_settings[SettingsKeys.X2TEXT_ADAPTER] - summarize_prompt: str = tool_settings[SettingsKeys.SUMMARIZE_PROMPT] - run_id: str = usage_kwargs.get(UsageKwargs.RUN_ID) - extract_file_path = tool_data_dir / SettingsKeys.EXTRACT - summarize_file_path = tool_data_dir / SettingsKeys.SUMMARIZE - - summarized_context = "" - self.stream_log( - f"Checking if summarized context exists at '{summarize_file_path}'..." - ) - if self.workflow_filestorage.exists(summarize_file_path): - summarized_context = self.workflow_filestorage.read( - path=summarize_file_path, mode="r" - ) - if not summarized_context: - context = "" - context = self.workflow_filestorage.read(path=extract_file_path, mode="r") - prompt_keys = [] - for output in outputs: - prompt_keys.append(output[SettingsKeys.NAME]) - output[SettingsKeys.EMBEDDING] = embedding_instance_id - output[SettingsKeys.VECTOR_DB] = vector_db_instance_id - output[SettingsKeys.X2TEXT_ADAPTER] = x2text_instance_id - output[SettingsKeys.CHUNK_SIZE] = 0 - output[SettingsKeys.CHUNK_OVERLAP] = 0 - self.stream_log("Summarized context not found, summarizing...") - payload = { - SettingsKeys.RUN_ID: run_id, - SettingsKeys.LLM_ADAPTER_INSTANCE_ID: llm_adapter_instance_id, - SettingsKeys.SUMMARIZE_PROMPT: summarize_prompt, - SettingsKeys.CONTEXT: context, - SettingsKeys.PROMPT_KEYS: prompt_keys, - } - structure_output = responder.summarize(payload=payload) - summarized_context = structure_output.get(SettingsKeys.DATA, "") - self.stream_log(f"Writing summarized context to '{summarize_file_path}'") - self.workflow_filestorage.write( - path=summarize_file_path, mode="w", data=summarized_context - ) - - summarize_file_hash: str = self.workflow_filestorage.get_hash_from_file( - path=summarize_file_path - ) - return str(summarize_file_path), summarize_file_hash - - -if __name__ == "__main__": - args = sys.argv[1:] - tool = StructureTool.from_tool_args(args=args) - ToolEntrypoint.launch(tool=tool, args=args) diff --git a/tools/structure/src/utils.py b/tools/structure/src/utils.py deleted file mode 100644 index a374918aba..0000000000 --- a/tools/structure/src/utils.py +++ /dev/null @@ -1,100 +0,0 @@ -from typing import Any - -from json_repair import repair_json - - -def json_to_markdown(data: Any, level: int = 0, parent_key: str = "") -> str: - markdown = "" - indent = " " * level # Increase indentation by 2 for nested levels - - if isinstance(data, dict): - for key, value in data.items(): - if isinstance(value, (dict, list)): - # If value is a dict or list, make it expandable - markdown += f"{indent}- **{key}**:\n" - markdown += json_to_markdown(value, level + 1, key) - else: - markdown += f"{indent}- **{key}**: {value}\n" - elif isinstance(data, list): - for index, item in enumerate(data, 1): - # Use parent key for list item naming - # Fall back to "Item" if parent_key is empty - # TODO: Determine child key using parent key for all plural combinations - item_label = ( - f"{parent_key[:-1] if parent_key.endswith('s') else parent_key} {index}" - if parent_key - else f"Item {index}" - ) - markdown += f"{indent}- **{item_label}**\n" - markdown += json_to_markdown(item, level + 1, parent_key) - else: - markdown += f"{indent}- {data}\n" - - return markdown - - -def repair_json_with_best_structure(json_str: str) -> Any: - """Repair and parse a potentially malformed JSON string with optimal structure detection. - - This function attempts to repair and parse a JSON string using two different strategies - and returns the result that produces the most useful data structure. It handles cases - where the input might be incomplete, malformed, or ambiguous JSON. - - The function tries two parsing approaches: - 1. Parse the JSON string as-is - 2. Parse the JSON string wrapped in array brackets [...] - - It then intelligently selects the best result based on the following logic: - - If both results are strings (failed to parse as objects), return the as-is result - - If one result is a string and the other is an object/array, return the object/array - - If wrapping produces a single-element list that equals the as-is result, return as-is - - If as-is produces an object/array and wrapping produces multiple elements, prefer wrapped - - Otherwise, prefer the as-is result - - Args: - json_str: A string containing potentially malformed JSON data. Can be a complete - JSON object, array, or partial JSON that needs repair. - - Returns: - The parsed JSON structure (dict, list, str, or other JSON-compatible type) that - represents the most meaningful interpretation of the input string. The return type - depends on the input and which parsing strategy produces the better result. - - Example: - >>> repair_json_with_best_structure('{"name": "John", "age": 30}') - {'name': 'John', 'age': 30} - - >>> repair_json_with_best_structure('{"incomplete": "object"') - {'incomplete': 'object'} - - >>> repair_json_with_best_structure('{"a": 1}{"b": 2}') - [{'a': 1}, {'b': 2}] - - Note: - This function is specifically designed for the structure-tool and uses the - json_repair library's repair_json function with return_objects=True and - ensure_ascii=False parameters. - """ - parsed_as_is = repair_json(json_str=json_str, return_objects=True, ensure_ascii=False) - parsed_with_wrap = repair_json( - "[" + json_str + "]", return_objects=True, ensure_ascii=False - ) - - if all(isinstance(x, str) for x in (parsed_as_is, parsed_with_wrap)): - return parsed_as_is - - if isinstance(parsed_as_is, str): - return parsed_with_wrap - if isinstance(parsed_with_wrap, str): - return parsed_as_is - - if isinstance(parsed_with_wrap, list) and len(parsed_with_wrap) == 1: - if parsed_with_wrap[0] == parsed_as_is: - return parsed_as_is - - if isinstance(parsed_as_is, (dict, list)): - if isinstance(parsed_with_wrap, list) and len(parsed_with_wrap) > 1: - return parsed_with_wrap - return parsed_as_is - - return parsed_with_wrap diff --git a/tools/text_extractor/.dockerignore b/tools/text_extractor/.dockerignore deleted file mode 100644 index c26352afcc..0000000000 --- a/tools/text_extractor/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -venv/ -.venv/ -.env diff --git a/tools/text_extractor/.gitignore b/tools/text_extractor/.gitignore deleted file mode 100644 index 3a8816c9ee..0000000000 --- a/tools/text_extractor/.gitignore +++ /dev/null @@ -1,162 +0,0 @@ -# Byte-compiled / optimized / DLL files -__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 -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.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 - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm-project.org/#use-with-ide -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# 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 -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ diff --git a/tools/text_extractor/Dockerfile b/tools/text_extractor/Dockerfile deleted file mode 100644 index ea3abef48b..0000000000 --- a/tools/text_extractor/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -FROM python:3.12-slim-trixie - -LABEL maintainer="Zipstack Inc." - -ENV \ - # Extended PYTHONPATH to include all unstract module source directories - APP_HOME=/app \ - BUILD_PACKAGES_PATH=unstract \ - # Increase timeout for large packages (flipt-client is ~45MB) - PIP_DEFAULT_TIMEOUT=120 - -# Install dependencies for unstructured library's partition -RUN apt-get update && apt-get --no-install-recommends -y install dumb-init git libmagic-dev poppler-utils\ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install --no-cache-dir -U pip -# Set the working directory in the container -WORKDIR ${APP_HOME} -COPY tools/text_extractor/requirements.txt /app/ - -# Copy specific subdirectories while preserving structure -COPY ${BUILD_PACKAGES_PATH}/sdk1 /unstract/sdk1 -COPY ${BUILD_PACKAGES_PATH}/core /unstract/core -COPY ${BUILD_PACKAGES_PATH}/flags /unstract/flags - -RUN pip install --no-cache-dir -r requirements.txt && \ - pip install --no-cache-dir \ - opentelemetry-distro \ - opentelemetry-exporter-otlp \ - platformdirs>=3.0.0 \ - && pip install opentelemetry-instrumentation-openai \ - && opentelemetry-bootstrap -a install \ - && pip uninstall -y opentelemetry-instrumentation-openai-v2 - - -# Copy the contents of your project directory into the container at /app -COPY tools/text_extractor/src /app/src/ -WORKDIR /app/src - - -ENTRYPOINT ["opentelemetry-instrument", "python", "main.py"] diff --git a/tools/text_extractor/README.md b/tools/text_extractor/README.md deleted file mode 100644 index 65ef08437a..0000000000 --- a/tools/text_extractor/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Text Extractor Tool - -The Text Extractor Tool is a powerful tool designed to extract text from documents. -In other words, it converts documents into their text versions. -For example, it can convert PDF to text, image to text, etc. - -## Required Environment Variables - -| Variable | Description | -| -------------------------- |----------------------------------------------------------------------------| -| `PLATFORM_SERVICE_HOST` | The host where the platform service is running | -| `PLATFORM_SERVICE_PORT` | The port where the service is listening | -| `PLATFORM_SERVICE_API_KEY` | The API key for the platform | -| `EXECUTION_DATA_DIR` | The directory in the filesystem which contains contents for tool execution | -| `X2TEXT_HOST` | The host where the x2text service is running | -| `X2TEXT_PORT` | The port where the x2text service is listening | - -## Setting Up a Dev Environment - -1. Setup a virtual environment and activate it: - -```commandline -python -m venv .venv -source .venv/bin/activate -``` -2. Install the dependencies for the tool. - - Two Options - - Install by Pypi version - ```commandline - pip install -r requirements.txt - ``` - - To use the local development version of the [unstract-sdk](https://pypi.org/project/unstract-sdk/) install it from the local repository. - Replace the path with the path to your local repository - ```commandline - pip install -e ~/path_to_repo/sdks/. - ``` - -#### Tool execution preparation - -1. Load the environment variables for the tool. -Make a copy of the `sample.env` file and name it `.env`. Fill in the required values. -They get loaded with [python-dotenv](https://pypi.org/project/python-dotenv/) through the SDK. - -2. Update the tool's `data_dir` marked by the `EXECUTION_DATA_DIR` env. This has to be done before each tool execution since the tool updates the `INFILE` and `METADATA.json`. - -#### Run SPEC command - -Represents the JSON schema for the runtime configurable `settings` of a tool -```commandline -python main.py --command SPEC -``` - -#### Run PROPERTIES command - -Describes some metadata for the tool such as its `version`, `description`, `inputs` and `outputs` -```commandline -python main.py --command PROPERTIES -``` - -#### Run ICON command - -Returns the SVG icon for the tool, used by Unstract's frontend -```commandline -python main.py --command ICON -``` - -#### Run VARIABLES command - -Represents the runtime variables or envs that will be used by the tool -```commandline -python main.py --command VARIABLES -``` - -#### Run RUN command - - -The schema of the JSON required for settings can be found by running the [SPEC](#run-spec-command) command. Alternatively if you have access to the code base, it is located in the `config` folder as `spec.json`. - -```commandline -python main.py \ - --command RUN \ - --settings '{ - "extractorId": "" - }' \ - --workflow-id '00000000-0000-0000-0000-000000000000' \ - --log-level DEBUG - -``` -### Testing the tool from its docker image - -Build the tool docker image from the folder containing the `Dockerfile` with -```commandline -docker build -t unstract/tool-example:0.0.1 . -``` - -Make sure the directory pointed by `EXECUTION_DATA_DIR` has the required information for the tool to run and -necessary services like the `platform-service` is up. -To test the tool from its docker image, run the following command - -```commandline -docker run -it \ - --network unstract-network \ - --env-file .env \ - -v "$(pwd)"/data_dir:/app/data_dir \ - unstract/tool-example:0.0.1 \ - --command RUN \ - --settings '{ - "extractorId": "" - }' \ - --workflow-id '00000000-0000-0000-0000-000000000000' \ - --log-level DEBUG - -``` diff --git a/tools/text_extractor/__init__.py b/tools/text_extractor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/text_extractor/requirements.txt b/tools/text_extractor/requirements.txt deleted file mode 100644 index 4347f319b3..0000000000 --- a/tools/text_extractor/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Add your dependencies here - -# Required for all unstract tools -# aws alone is needed here -# because tools use transient temporary storage. - --e file:/unstract/core --e file:/unstract/sdk1[aws] --e file:/unstract/flags diff --git a/tools/text_extractor/sample.env b/tools/text_extractor/sample.env deleted file mode 100644 index de3ab3e916..0000000000 --- a/tools/text_extractor/sample.env +++ /dev/null @@ -1,14 +0,0 @@ -PLATFORM_SERVICE_HOST= -PLATFORM_SERVICE_PORT= -PLATFORM_SERVICE_API_KEY= -EXECUTION_DATA_DIR= -# X2TEXT service -X2TEXT_HOST= -X2TEXT_PORT= - -# File System Configuration for Workflow Execution -# Directory path for execution data storage -# (e.g., bucket/execution/org_id/workflow_id/execution_id) -EXECUTION_DATA_DIR= -# Storage provider for Workflow Execution (e.g., minio, S3) -WORKFLOW_EXECUTION_FILE_STORAGE_CREDENTIALS='{"provider":"minio","credentials"={"endpoint_url":"http://localhost:9000","key":"XXX","secret":"XXX"}}' diff --git a/tools/text_extractor/src/config/icon.svg b/tools/text_extractor/src/config/icon.svg deleted file mode 100644 index e3a507bafa..0000000000 --- a/tools/text_extractor/src/config/icon.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tools/text_extractor/src/config/properties.json b/tools/text_extractor/src/config/properties.json deleted file mode 100644 index a3d3c336f7..0000000000 --- a/tools/text_extractor/src/config/properties.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "schemaVersion": "0.0.1", - "displayName": "Text Extractor", - "functionName": "text_extractor", - "toolVersion": "0.0.75", - "description": "The Text Extractor is a powerful tool designed to convert documents to its text form or Extract texts from documents", - "input": { - "description": "Document" - }, - "output": { - "description": "structured json output" - }, - "result": { - "type": "TXT", - "description": "Text format of input", - "schema": {} - }, - "adapter": { - "textExtractors": [ - { - "isEnabled": true, - "adapterId": "extractorId", - "title": "Text Extraction Adapter", - "isRequired": true, - "description": "Adapter to extract" - } - ] - }, - "ioCompatibility": { - "api": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": { - "sync": true - } - }, - "file": { - "sourceSupport": true, - "destinationSupport": true, - "additionalArgs": {} - }, - "db": { - "destinationSupport": true, - "additionalArgs": {} - } - }, - "restrictions": { - "maxFileSize": "200MB", - "allowedFileTypes": [ - "*" - ] - } -} diff --git a/tools/text_extractor/src/config/runtime_variables.json b/tools/text_extractor/src/config/runtime_variables.json deleted file mode 100644 index 38177eee0b..0000000000 --- a/tools/text_extractor/src/config/runtime_variables.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "title": "Runtime Variables", - "description": "Runtime Variables for text extractor", - "type": "object", - "required": [], - "properties": { - "X2TEXT_HOST": { - "type": "string", - "title": "X2Text service host", - "description": "The host where the x2text service is running" - }, - "X2TEXT_PORT": { - "type": "string", - "title": "X2Text service port", - "description": "The port where the x2text service is running" - } - } -} diff --git a/tools/text_extractor/src/config/spec.json b/tools/text_extractor/src/config/spec.json deleted file mode 100644 index d55b27cf15..0000000000 --- a/tools/text_extractor/src/config/spec.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Text Extractor tool settings", - "description": "Text extraction from documents", - "type": "object", - "required": [], - "properties": {} -} diff --git a/tools/text_extractor/src/example_package/__init__.py b/tools/text_extractor/src/example_package/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/text_extractor/src/main.py b/tools/text_extractor/src/main.py deleted file mode 100644 index c72dfd5e98..0000000000 --- a/tools/text_extractor/src/main.py +++ /dev/null @@ -1,109 +0,0 @@ -import ast -import sys -from pathlib import Path -from typing import Any - -from unstract.sdk1.constants import LogState, UsageKwargs -from unstract.sdk1.tool.base import BaseTool -from unstract.sdk1.tool.entrypoint import ToolEntrypoint -from unstract.sdk1.x2txt import TextExtractionResult, X2Text - - -class TextExtractor(BaseTool): - def validate(self, input_file: str, settings: dict[str, Any]) -> None: - """Validate the input file and settings. - - Args: - input_file (str): The path to the input file. - settings (dict[str, Any]): The settings for the tool. - """ - text_extraction_adapter_id = settings["extractorId"] - if not text_extraction_adapter_id: - self.stream_error_and_exit( - "Adaptor id not found, please select adaptor for extractor tool" - ) - - def run( - self, - settings: dict[str, Any], - input_file: str, - output_dir: str, - ) -> None: - """Run the text extraction process. - - Args: - settings (dict[str, Any]): The settings for the tool. - input_file (str): The path to the input file. - output_dir (str): The path to the output directory. - - Raises: - Exception: If there is an error creating/writing the output file. - - Returns: - None - """ - text_extraction_adapter_id = settings["extractorId"] - - self.stream_log( - f"Extractor ID: {text_extraction_adapter_id} " - "has been retrieved from settings." - ) - - input_log = f"Processing file: \n\n`{self.source_file_name}`" - self.stream_update(input_log, state=LogState.INPUT_UPDATE) - - usage_kwargs: dict[Any, Any] = dict() - usage_kwargs[UsageKwargs.RUN_ID] = self.file_execution_id - usage_kwargs[UsageKwargs.FILE_NAME] = self.source_file_name - - text_extraction_adapter = X2Text( - tool=self, - adapter_instance_id=text_extraction_adapter_id, - usage_kwargs=usage_kwargs, - ) - self.stream_log("Text extraction adapter has been created successfully.") - extraction_result: TextExtractionResult = text_extraction_adapter.process( - input_file_path=input_file, fs=self.workflow_filestorage, tags=self.tags - ) - extracted_text = self.convert_to_actual_string(extraction_result.extracted_text) - - self.stream_log("Text has been extracted successfully.") - - first_5_lines = "\n\n".join(extracted_text.split("\n")[:5]) - output_log = f"### Text\n\n```text\n{first_5_lines}\n```\n\n...(truncated)" - self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) - - try: - self.stream_log("Preparing to write the extracted text.") - if self.source_file_name: - output_path = Path(output_dir) / f"{Path(self.source_file_name).stem}.txt" - self.workflow_filestorage.write( - path=output_path, mode="w", data=extracted_text - ) - - self.stream_log("Tool output written successfully.") - else: - self.stream_error_and_exit( - "Error creating/writing output file: source_file_name not found" - ) - except Exception as e: - self.stream_error_and_exit(f"Error creating/writing output file: {e}") - self.write_tool_result(data=extracted_text) - - def convert_to_actual_string(self, text: Any) -> str: - if isinstance(text, bytes): - return text.decode("utf-8") - elif isinstance(text, str): - if text.startswith("b'") and text.endswith("'"): - bytes_text: bytes = ast.literal_eval(text) - return bytes_text.decode("utf-8") - else: - return text - else: - return str(text) - - -if __name__ == "__main__": - args = sys.argv[1:] - tool = TextExtractor.from_tool_args(args=args) - ToolEntrypoint.launch(tool=tool, args=args) diff --git a/tools/text_extractor/tests/__init__.py b/tools/text_extractor/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/unstract/sdk1/src/unstract/sdk1/prompt.py b/unstract/sdk1/src/unstract/sdk1/prompt.py deleted file mode 100644 index c648506209..0000000000 --- a/unstract/sdk1/src/unstract/sdk1/prompt.py +++ /dev/null @@ -1,260 +0,0 @@ -import functools -import logging -from collections.abc import Callable -from typing import Any, ParamSpec, TypeVar - -import requests -from requests import ConnectionError, RequestException, Response -from unstract.sdk1.constants import MimeType, RequestHeader, ToolEnv -from unstract.sdk1.platform import PlatformHelper -from unstract.sdk1.tool.base import BaseTool -from unstract.sdk1.utils.common import log_elapsed -from unstract.sdk1.utils.retry_utils import retry_prompt_service_call - -logger = logging.getLogger(__name__) - -P = ParamSpec("P") -R = TypeVar("R") - - -def handle_service_exceptions(context: str) -> Callable[[Callable[P, R]], Callable[P, R]]: - """Decorator to handle exceptions in PromptTool service calls. - - Args: - context (str): Context string describing where the error occurred - Returns: - Callable: Decorated function that handles service exceptions - """ - - def decorator(func: Callable[P, R]) -> Callable[P, R]: - @functools.wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: - try: - return func(*args, **kwargs) - except ConnectionError as e: - msg = f"Error while {context}. Unable to connect to prompt service." - logger.error(f"{msg}\n{e}") - args[0].tool.stream_error_and_exit(msg, e) - return None - except RequestException as e: - error_message = str(e) - response = getattr(e, "response", None) - if response is not None: - if ( - MimeType.JSON in response.headers.get("Content-Type", "").lower() - and "error" in response.json() - ): - error_message = response.json()["error"] - elif response.text: - error_message = response.text - msg = f"Error while {context}. {error_message}" - args[0].tool.stream_error_and_exit(msg, e) - return None - except Exception as e: - # Handle any other unexpected exceptions - msg = f"Error while {context}. An unexpected error occurred" - logger.error(f"{msg}: {type(e).__name__}: {str(e)}", exc_info=True) - args[0].tool.stream_error_and_exit(msg, e) - return None - - return wrapper - - return decorator - - -class PromptTool: - """Class to handle prompt service methods for Unstract Tools.""" - - def __init__( - self, - tool: BaseTool, - prompt_host: str, - prompt_port: str, - is_public_call: bool = False, - request_id: str | None = None, - ) -> None: - """Class to interact with prompt-service. - - Args: - tool (AbstractTool): Instance of AbstractTool - prompt_host (str): Host of platform service - prompt_port (str): Port of platform service - is_public_call (bool): Whether the call is public. Defaults to False - """ - self.tool = tool - self.base_url = PlatformHelper.get_platform_base_url(prompt_host, prompt_port) - self.is_public_call = is_public_call - self.request_id = request_id - if not is_public_call: - self.bearer_token = tool.get_env_or_die(ToolEnv.PLATFORM_API_KEY) - - @log_elapsed(operation="ANSWER_PROMPTS") - @handle_service_exceptions("answering prompt(s)") - def answer_prompt( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> dict[str, Any]: - url_path = "answer-prompt" - if self.is_public_call: - url_path = "answer-prompt-public" - return self._call_service( - url_path=url_path, payload=payload, params=params, headers=headers - ) - - @log_elapsed(operation="INDEX") - @handle_service_exceptions("indexing") - def index( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> str: - url_path = "index" - if self.is_public_call: - url_path = "index-public" - prompt_service_response = self._call_service( - url_path=url_path, - payload=payload, - params=params, - headers=headers, - ) - return prompt_service_response.get("doc_id") - - @log_elapsed(operation="EXTRACT") - @handle_service_exceptions("extracting") - def extract( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> dict[str, Any]: - url_path = "extract" - if self.is_public_call: - url_path = "extract-public" - prompt_service_response = self._call_service( - url_path=url_path, - payload=payload, - params=params, - headers=headers, - ) - return prompt_service_response.get("extracted_text") - - @log_elapsed(operation="SINGLE_PASS_EXTRACTION") - @handle_service_exceptions("single pass extraction") - def single_pass_extraction( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> dict[str, Any]: - return self._call_service( - url_path="single-pass-extraction", - payload=payload, - params=params, - headers=headers, - ) - - @log_elapsed(operation="SUMMARIZATION") - @handle_service_exceptions("summarizing") - def summarize( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> dict[str, Any]: - return self._call_service( - url_path="summarize", - payload=payload, - params=params, - headers=headers, - ) - - @log_elapsed(operation="AGENTIC_EXTRACTION") - @handle_service_exceptions("executing agentic extraction") - def agentic_extraction( - self, - payload: dict[str, Any], - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - ) -> dict[str, Any]: - """Execute agentic extraction via prompt service. - - Args: - payload: Extraction payload containing project_id, json_schema, - prompt_text, adapter IDs, etc. - params: Optional query parameters - headers: Optional headers - - Returns: - dict: Extraction results with data, metadata, and metrics - """ - return self._call_service( - url_path="agentic/extract", - payload=payload, - params=params, - headers=headers, - ) - - def _get_headers(self, headers: dict[str, str] | None = None) -> dict[str, str]: - """Get default headers for requests. - - Returns: - dict[str, str]: Default headers including request ID and authorization - """ - request_headers = {RequestHeader.REQUEST_ID: self.request_id} - if self.is_public_call: - return request_headers - request_headers.update( - {RequestHeader.AUTHORIZATION: f"Bearer {self.bearer_token}"} - ) - - if headers: - request_headers.update(headers) - return request_headers - - @retry_prompt_service_call - def _call_service( - self, - url_path: str, - payload: dict[str, Any] | None = None, - params: dict[str, str] | None = None, - headers: dict[str, str] | None = None, - method: str = "POST", - ) -> dict[str, Any]: - """Communicates to prompt service to fetch response for the prompt. - - Only POST calls are made to prompt-service though functionality exists. - This method automatically retries on connection errors with exponential backoff. - - Retry behavior is configurable via environment variables: - - PROMPT_SERVICE_MAX_RETRIES (default: 3) - - PROMPT_SERVICE_BASE_DELAY (default: 1.0s) - - PROMPT_SERVICE_MULTIPLIER (default: 2.0) - - PROMPT_SERVICE_JITTER (default: true) - - Args: - url_path (str): URL path to the service endpoint - payload (dict, optional): Payload to send in the request body - params (dict, optional): Query parameters to include in the request - headers (dict, optional): Headers to include in the request - method (str): HTTP method to use for the request (GET or POST) - - Returns: - dict: Response from the prompt service - """ - url: str = f"{self.base_url}/{url_path}" - req_headers = self._get_headers(headers) - response: Response = Response() - if method.upper() == "POST": - response = requests.post( - url=url, json=payload, params=params, headers=req_headers - ) - elif method.upper() == "GET": - response = requests.get(url=url, params=params, headers=req_headers) - else: - raise ValueError(f"Unsupported HTTP method: {method}") - - response.raise_for_status() - return response.json() diff --git a/unstract/sdk1/src/unstract/sdk1/utils/retry_utils.py b/unstract/sdk1/src/unstract/sdk1/utils/retry_utils.py index 0a13331474..a4bf5d63b7 100644 --- a/unstract/sdk1/src/unstract/sdk1/utils/retry_utils.py +++ b/unstract/sdk1/src/unstract/sdk1/utils/retry_utils.py @@ -516,10 +516,3 @@ def create_retry_decorator( # - PLATFORM_SERVICE_MULTIPLIER (default: 2.0) # - PLATFORM_SERVICE_JITTER (default: true) retry_platform_service_call = create_retry_decorator("PLATFORM_SERVICE") - -# Retry configured through below envs. -# - PROMPT_SERVICE_MAX_RETRIES (default: 3) -# - PROMPT_SERVICE_BASE_DELAY (default: 1.0s) -# - PROMPT_SERVICE_MULTIPLIER (default: 2.0) -# - PROMPT_SERVICE_JITTER (default: true) -retry_prompt_service_call = create_retry_decorator("PROMPT_SERVICE") diff --git a/unstract/sdk1/tests/conftest.py b/unstract/sdk1/tests/conftest.py index b3858b7e2f..f01ce56e5e 100644 --- a/unstract/sdk1/tests/conftest.py +++ b/unstract/sdk1/tests/conftest.py @@ -25,10 +25,6 @@ def clean_env(monkeypatch: MonkeyPatch) -> MonkeyPatch: "PLATFORM_SERVICE_BASE_DELAY", "PLATFORM_SERVICE_MULTIPLIER", "PLATFORM_SERVICE_JITTER", - "PROMPT_SERVICE_MAX_RETRIES", - "PROMPT_SERVICE_BASE_DELAY", - "PROMPT_SERVICE_MULTIPLIER", - "PROMPT_SERVICE_JITTER", ] for var in env_vars: monkeypatch.delenv(var, raising=False) diff --git a/unstract/sdk1/tests/test_prompt.py b/unstract/sdk1/tests/test_prompt.py deleted file mode 100644 index 798db9106c..0000000000 --- a/unstract/sdk1/tests/test_prompt.py +++ /dev/null @@ -1,174 +0,0 @@ -"""Integration tests for prompt module with retry logic.""" - -from typing import Any, Self -from unittest.mock import MagicMock, Mock, patch - -import pytest -from pytest import MonkeyPatch -from requests.exceptions import ConnectionError, Timeout -from unstract.sdk1.prompt import PromptTool - - -class TestPromptToolRetry: - """Tests for PromptTool retry functionality.""" - - @pytest.fixture - def mock_tool(self: Self) -> MagicMock: - """Create a mock tool for testing.""" - tool = MagicMock() - tool.get_env_or_die.side_effect = lambda key: { - "PLATFORM_API_KEY": "test-api-key", - }.get(key, "mock-value") - tool.stream_log = MagicMock() - tool.stream_error_and_exit = MagicMock() - return tool - - @pytest.fixture - def prompt_tool(self: Self, mock_tool: MagicMock) -> PromptTool: - """Create a PromptTool instance.""" - return PromptTool( - tool=mock_tool, - prompt_host="http://localhost", - prompt_port="3003", - is_public_call=False, - request_id="test-request-id", - ) - - def test_success_on_first_attempt( - self: Self, prompt_tool: PromptTool, clean_env: MonkeyPatch - ) -> None: - """Test successful service call on first attempt.""" - expected_response = {"result": "success"} - payload = {"prompt": "test"} - - with patch("requests.post") as mock_post: - mock_response = Mock() - mock_response.json.return_value = expected_response - mock_response.raise_for_status = Mock() - mock_post.return_value = mock_response - - result = prompt_tool._call_service("answer-prompt", payload=payload) - - assert result == expected_response - assert mock_post.call_count == 1 - - @pytest.mark.parametrize( - "error_type,error_instance", - [ - ("ConnectionError", ConnectionError("Connection failed")), - ("Timeout", Timeout("Request timed out")), - ], - ) - def test_retry_on_errors( - self: Self, - prompt_tool: PromptTool, - error_type: str, - error_instance: Exception, - clean_env: MonkeyPatch, - ) -> None: - """Test service retries on ConnectionError and Timeout.""" - expected_response = {"result": "success"} - payload = {"prompt": "test"} - - with patch("requests.post") as mock_post: - mock_response = Mock() - mock_response.json.return_value = expected_response - mock_response.raise_for_status = Mock() - - mock_post.side_effect = [ - error_instance, - mock_response, - ] - - result = prompt_tool._call_service("answer-prompt", payload=payload) - - assert result == expected_response - assert mock_post.call_count == 2 - - @pytest.mark.slow - def test_max_retries_exceeded( - self: Self, mock_tool: MagicMock, clean_env: MonkeyPatch - ) -> None: - """Test service call fails after exceeding max retries.""" - prompt_tool = PromptTool( - tool=mock_tool, - prompt_host="http://localhost", - prompt_port="3003", - is_public_call=False, - request_id="test-request-id", - ) - - payload = {"prompt": "test"} - - with patch("requests.post") as mock_post: - mock_post.side_effect = ConnectionError("Persistent failure") - - # Exception handled by decorator - with pytest.raises(ConnectionError): - prompt_tool._call_service("answer-prompt", payload=payload) - - # Default: 3 retries + 1 initial = 4 attempts - assert mock_post.call_count == 4 - - @pytest.mark.parametrize( - "method_name,payload", - [ - ("answer_prompt", {"prompts": ["test"]}), - ("index", {"document": "test"}), - ("extract", {"doc_id": "123"}), - ("summarize", {"text": "test"}), - ], - ) - def test_wrapper_methods_retry( - self: Self, - prompt_tool: PromptTool, - method_name: str, - payload: dict[str, Any], - clean_env: MonkeyPatch, - ) -> None: - """Test that wrapper methods inherit retry behavior.""" - expected_response = { - "answers": ["result"], - "doc_id": "doc-123", - "extracted_text": "text", - "summary": "summary", - } - - with patch("requests.post") as mock_post: - mock_response = Mock() - mock_response.json.return_value = expected_response - mock_response.raise_for_status = Mock() - - mock_post.side_effect = [ - ConnectionError("Transient failure"), - mock_response, - ] - - getattr(prompt_tool, method_name)(payload) - - assert mock_post.call_count == 2 - - @pytest.mark.slow - def test_error_handling_with_retry( - self: Self, mock_tool: MagicMock, clean_env: MonkeyPatch - ) -> None: - """Test error handling decorator works with retry.""" - prompt_tool = PromptTool( - tool=mock_tool, - prompt_host="http://localhost", - prompt_port="3003", - is_public_call=False, - request_id="test-request-id", - ) - - payload = {"prompt": "test"} - - with patch("requests.post") as mock_post: - mock_post.side_effect = ConnectionError("Persistent failure") - - # Error handler should catch after all retries - result = prompt_tool.answer_prompt(payload) - - # handle_service_exceptions decorator calls stream_error_and_exit - assert result is None - prompt_tool.tool.stream_error_and_exit.assert_called() diff --git a/unstract/sdk1/tests/utils/test_retry_utils.py b/unstract/sdk1/tests/utils/test_retry_utils.py index cbf73c731a..d0b90d2f80 100644 --- a/unstract/sdk1/tests/utils/test_retry_utils.py +++ b/unstract/sdk1/tests/utils/test_retry_utils.py @@ -12,7 +12,6 @@ create_retry_decorator, is_retryable_error, retry_platform_service_call, - retry_prompt_service_call, retry_with_exponential_backoff, ) @@ -485,10 +484,6 @@ def test_retry_platform_service_call_exists(self) -> None: """Test that retry_platform_service_call decorator exists.""" assert retry_platform_service_call is not None - def test_retry_prompt_service_call_exists(self) -> None: - """Test that retry_prompt_service_call decorator exists.""" - assert retry_prompt_service_call is not None - def test_platform_service_decorator_retries_on_connection_error( self, clean_env: MonkeyPatch ) -> None: @@ -504,20 +499,6 @@ def test_platform_service_decorator_retries_on_connection_error( assert result == "success" assert mock_func.call_count == 2 - def test_prompt_service_decorator_retries_on_timeout( - self, clean_env: MonkeyPatch - ) -> None: - """Test prompt service decorator retries Timeout.""" - mock_func = Mock( - __name__="test_func", side_effect=[Timeout("Timed out"), "success"] - ) - - decorated_func = retry_prompt_service_call(mock_func) - - result = decorated_func() - - assert result == "success" - assert mock_func.call_count == 2 class TestRetryLogging: diff --git a/unstract/workflow-execution/src/unstract/workflow_execution/constants.py b/unstract/workflow-execution/src/unstract/workflow_execution/constants.py index e7b9d46167..3786706cd2 100644 --- a/unstract/workflow-execution/src/unstract/workflow_execution/constants.py +++ b/unstract/workflow-execution/src/unstract/workflow_execution/constants.py @@ -14,8 +14,6 @@ class ToolRuntimeVariable: PLATFORM_HOST = "PLATFORM_SERVICE_HOST" PLATFORM_PORT = "PLATFORM_SERVICE_PORT" PLATFORM_SERVICE_API_KEY = "PLATFORM_SERVICE_API_KEY" - PROMPT_HOST = "PROMPT_HOST" - PROMPT_PORT = "PROMPT_PORT" X2TEXT_HOST = "X2TEXT_HOST" X2TEXT_PORT = "X2TEXT_PORT" ADAPTER_LLMW_POLL_INTERVAL = "ADAPTER_LLMW_POLL_INTERVAL" diff --git a/unstract/workflow-execution/src/unstract/workflow_execution/tools_utils.py b/unstract/workflow-execution/src/unstract/workflow_execution/tools_utils.py index 6778eafe17..040c9485cb 100644 --- a/unstract/workflow-execution/src/unstract/workflow_execution/tools_utils.py +++ b/unstract/workflow-execution/src/unstract/workflow_execution/tools_utils.py @@ -44,8 +44,6 @@ def __init__( self.platform_service_port = ToolsUtils.get_env( ToolRV.PLATFORM_PORT, raise_exception=True ) - self.prompt_host = ToolsUtils.get_env(ToolRV.PROMPT_HOST, raise_exception=True) - self.prompt_port = ToolsUtils.get_env(ToolRV.PROMPT_PORT, raise_exception=True) self.x2text_host = ToolsUtils.get_env(ToolRV.X2TEXT_HOST, raise_exception=True) self.x2text_port = ToolsUtils.get_env(ToolRV.X2TEXT_PORT, raise_exception=True) self.llmw_poll_interval = ToolsUtils.get_env( @@ -232,8 +230,6 @@ def get_tool_environment_variables(self) -> dict[str, Any]: ToolRV.PLATFORM_HOST: self.platform_service_host, ToolRV.PLATFORM_PORT: self.platform_service_port, ToolRV.PLATFORM_SERVICE_API_KEY: self.platform_service_api_key, - ToolRV.PROMPT_HOST: self.prompt_host, - ToolRV.PROMPT_PORT: self.prompt_port, ToolRV.X2TEXT_HOST: self.x2text_host, ToolRV.X2TEXT_PORT: self.x2text_port, ToolRV.EXECUTION_BY_TOOL: True, diff --git a/workers/sample.env b/workers/sample.env index 5c0bce2d4b..d98bb9c986 100644 --- a/workers/sample.env +++ b/workers/sample.env @@ -254,10 +254,6 @@ NOTIFICATION_QUEUE_NAME=notifications PLATFORM_SERVICE_HOST=http://unstract-platform-service PLATFORM_SERVICE_PORT=3001 -# Prompt Service -PROMPT_HOST=http://unstract-prompt-service -PROMPT_PORT=3003 - # X2Text Service X2TEXT_HOST=http://unstract-x2text-service X2TEXT_PORT=3004 @@ -395,7 +391,6 @@ GOOGLE_OAUTH2_SECRET= # REDIS_HOST=localhost # CACHE_REDIS_HOST=localhost # PLATFORM_SERVICE_HOST=http://localhost -# PROMPT_HOST=http://localhost # X2TEXT_HOST=http://localhost # UNSTRACT_RUNNER_HOST=http://localhost # WORKFLOW_EXECUTION_FILE_STORAGE_CREDENTIALS={"provider": "minio", "credentials": {"endpoint_url": "http://localhost:9000", "key": "minio", "secret": "minio123"}} From 2e1bc5427457cb66e770da82d447a22bd3166c79 Mon Sep 17 00:00:00 2001 From: harini-venkataraman Date: Wed, 20 May 2026 18:43:56 +0530 Subject: [PATCH 02/10] [MISC] Remove prompt-service from tox.ini env_list The prompt-service directory was deleted in the prior commit but tox.ini still referenced it, which would break CI test runs. Co-Authored-By: Claude Opus 4.6 --- tox.ini | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tox.ini b/tox.ini index c684321ed7..9ac4f99623 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -env_list = py{312}, runner, sdk1, prompt-service +env_list = py{312}, runner, sdk1 requires = tox-uv>=0.2.0 @@ -46,17 +46,3 @@ commands_pre = commands = uv run pytest -v -m "not slow" --cov=src/unstract/sdk1 --cov-report=term --cov-report=html --md-report-verbose=1 --md-report --md-report-flavor gfm --md-report-output ../../sdk1-report.md -[testenv:prompt-service] -changedir = prompt-service -deps = uv -allowlist_externals= - sh - uv - pytest -commands_pre = - uv sync --group test -commands = - # --noconftest is required because the parent tests/conftest.py imports - # Flask blueprints which trigger the full adapter chain (pinecone etc.). - # Unit tests must not depend on integration fixtures. - uv run pytest src/unstract/prompt_service/tests/unit/ -v -m "not slow" --noconftest --md-report-verbose=1 --md-report --md-report-flavor gfm --md-report-output ../prompt-service-report.md From 7bdff5ad96d439725b523040ca94acfefb6a41a0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 13:14:55 +0000 Subject: [PATCH 03/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tox.ini | 1 - unstract/sdk1/tests/utils/test_retry_utils.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 9ac4f99623..dd13211aef 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,3 @@ commands_pre = uv sync --group test commands = uv run pytest -v -m "not slow" --cov=src/unstract/sdk1 --cov-report=term --cov-report=html --md-report-verbose=1 --md-report --md-report-flavor gfm --md-report-output ../../sdk1-report.md - diff --git a/unstract/sdk1/tests/utils/test_retry_utils.py b/unstract/sdk1/tests/utils/test_retry_utils.py index d0b90d2f80..227734414a 100644 --- a/unstract/sdk1/tests/utils/test_retry_utils.py +++ b/unstract/sdk1/tests/utils/test_retry_utils.py @@ -500,7 +500,6 @@ def test_platform_service_decorator_retries_on_connection_error( assert mock_func.call_count == 2 - class TestRetryLogging: """Tests for retry logging behavior.""" From 47749269d4781d6c15f75fcf60f6ad5d0053f66d Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Tue, 19 May 2026 14:35:03 +0530 Subject: [PATCH 04/10] UN-2888 [FIX] Add hook for setting default triad for invited users (#1877) * [FIX] Add hook for setting default adapters for invited users Add setup_default_adapters_for_user() hook to AuthenticationService and call it from set_user_organization() when an invited user joins an existing organization. This allows the cloud plugin to set up default triad adapters (LLM, embedding, vector DB, x2text) for invited users, fixing silent failures in API deployment creation. Co-Authored-By: Claude Opus 4.6 (1M context) * Update backend/account_v2/authentication_controller.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: Praveen Kumar * [FIX] Improve log message for setup_default_adapters_for_user Address review comment: log user email and explain that default adapters will not be set when the method is not implemented. Co-Authored-By: Claude Opus 4.6 (1M context) * [MISC] Rename Default Triad to Default LLM Profile in UI Update display label from "Default Triad" to "Default LLM Profile" in the page heading and side navigation menu. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Signed-off-by: Praveen Kumar Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Deepak K <89829542+Deepak-Kesavan@users.noreply.github.com> --- backend/account_v2/authentication_controller.py | 11 +++++++++++ backend/account_v2/authentication_service.py | 5 +++++ .../navigations/side-nav-bar/SideNavBar.jsx | 2 +- .../settings/default-triad/DefaultTriad.jsx | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/account_v2/authentication_controller.py b/backend/account_v2/authentication_controller.py index 19eac7baa9..56982ed9b7 100644 --- a/backend/account_v2/authentication_controller.py +++ b/backend/account_v2/authentication_controller.py @@ -213,6 +213,17 @@ def set_user_organization(self, request: Request, organization_id: str) -> Respo logger.info( f"New organization created with Id {organization_id}", ) + else: + try: + self.auth_service.setup_default_adapters_for_user( + organization=organization, user=user + ) + except MethodNotImplemented: + logger.info( + "setup_default_adapters_for_user not implemented, " + "default adapters will not be set for user %s", + user.email, + ) user_info: UserInfo | None = self.get_user_info(request) serialized_user_info = SetOrganizationsResponseSerializer(user_info).data diff --git a/backend/account_v2/authentication_service.py b/backend/account_v2/authentication_service.py index 591742821a..053a1e67af 100644 --- a/backend/account_v2/authentication_service.py +++ b/backend/account_v2/authentication_service.py @@ -266,6 +266,11 @@ def get_invitations(self, organization_id: str) -> list[MemberInvitation]: def frictionless_onboarding(self, organization: Organization, user: User) -> None: raise MethodNotImplemented() + def setup_default_adapters_for_user( + self, organization: Organization, user: User + ) -> None: + raise MethodNotImplemented() + def delete_invitation(self, organization_id: str, invitation_id: str) -> bool: raise MethodNotImplemented() diff --git a/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx b/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx index 164fdab145..542bea613c 100644 --- a/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx +++ b/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx @@ -125,7 +125,7 @@ const getSettingsMenuItems = (orgName, isAdmin) => [ }, { key: "triad", - label: "Default Triad", + label: "Default LLM Profile", path: `/${orgName}/settings/triad`, }, ...(manualReviewSettingsEnabled diff --git a/frontend/src/components/settings/default-triad/DefaultTriad.jsx b/frontend/src/components/settings/default-triad/DefaultTriad.jsx index 31cb045df6..141f630e4c 100644 --- a/frontend/src/components/settings/default-triad/DefaultTriad.jsx +++ b/frontend/src/components/settings/default-triad/DefaultTriad.jsx @@ -181,7 +181,7 @@ function DefaultTriad() { - Default Triad + Default LLM Profile
From fd6e3e71836992fa818be7b9f0cd1fc0da419c04 Mon Sep 17 00:00:00 2001 From: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com> Date: Tue, 19 May 2026 14:37:41 +0530 Subject: [PATCH 05/10] UN-3465 [FIX] Wrap set_user_organization in transaction.atomic (#1954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FIX] Wrap set_user_organization in transaction.atomic The new-org branch creates the org row, then calls frictionless onboarding and the initial platform key. Failures mid-flow leave an orphan org with no adapters or key, and subsequent logins skip onboarding entirely (gated on new_organization). Atomic ensures the org rolls back on any failure so retries get a clean fresh-org path. Co-Authored-By: Claude Opus 4.7 (1M context) * [MISC] Worktree skill — use --no-track to prevent accidental main pushes Without --no-track, a later `git push -u origin ` can be reported by the server as also fast-forwarding main, landing commits on main. * [FIX] Use logger.exception in authorization_callback Preserves the traceback when the OAuth callback hits the safety-net catch. Behaviour unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Athul <89829560+athul-rs@users.noreply.github.com> Co-authored-by: vishnuszipstack <117254672+vishnuszipstack@users.noreply.github.com> --- .claude/skills/worktree/SKILL.md | 5 ++++- backend/account_v2/authentication_controller.py | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.claude/skills/worktree/SKILL.md b/.claude/skills/worktree/SKILL.md index 50f8fb5e51..a1339a29b3 100644 --- a/.claude/skills/worktree/SKILL.md +++ b/.claude/skills/worktree/SKILL.md @@ -26,7 +26,10 @@ Create isolated worktrees for parallel development. Ends by providing commands t ```bash git fetch origin main mkdir -p "$(dirname "$WORKTREE_PATH")" - git worktree add -b "{branch}" "$WORKTREE_PATH" origin/main + # --no-track: do NOT set the new branch's upstream to origin/main. + # Without it, a later `git push -u origin {branch}` can be reported by + # the server as also fast-forwarding main, landing commits on main. + git worktree add --no-track -b "{branch}" "$WORKTREE_PATH" origin/main ``` 4. **Copy config files** diff --git a/backend/account_v2/authentication_controller.py b/backend/account_v2/authentication_controller.py index 56982ed9b7..d27bb50656 100644 --- a/backend/account_v2/authentication_controller.py +++ b/backend/account_v2/authentication_controller.py @@ -3,6 +3,7 @@ from django.conf import settings from django.contrib.auth import logout as django_logout +from django.db import transaction from django.db.utils import IntegrityError from django.http import HttpRequest from django.middleware import csrf @@ -100,8 +101,8 @@ def authorization_callback( return self.auth_service.handle_authorization_callback( request=request, backend=backend ) - except Exception as ex: - logger.error(f"Error while handling authorization callback: {ex}") + except Exception: + logger.exception("Error while handling authorization callback") return redirect("/error") def user_organizations(self, request: Request) -> Any: @@ -163,6 +164,7 @@ def user_organizations(self, request: Request) -> Any: return response + @transaction.atomic def set_user_organization(self, request: Request, organization_id: str) -> Response: user: User = request.user new_organization = False From c3c729d04b755949d5b021c6102dac1b2b349c10 Mon Sep 17 00:00:00 2001 From: vishnuszipstack <117254672+vishnuszipstack@users.noreply.github.com> Date: Tue, 19 May 2026 14:39:17 +0530 Subject: [PATCH 06/10] UN-3386 [FEAT] Add Prompt Studio HITL change indicator plugin slot (#1930) * UN-3386 [FEAT] Add Prompt Studio HITL change indicator plugin slot Wires up the host-side hooks for the prompt-change-indicator plugin (implementation lives in unstract-cloud): a dynamic-import slot in the prompt card Header for the indicator button, and a route at :orgName/review/readonly/:documentId for the read-only audit view. Both gates fall through gracefully when the plugin is absent (OSS). Co-Authored-By: Claude Opus 4.7 (1M context) * UN-3386 [FIX] Warn when ReadOnlyReviewPage loads without ReviewLayout Addresses review feedback: the readonly route nests inside ReviewLayout (manual-review plugin), so a deployment that ships prompt-change-indicator without manual-review would silently fail to register the route. Log a console.warn in that case to make the misconfiguration discoverable. Co-Authored-By: Claude Opus 4.7 (1M context) * UN-3386 [FIX] Surface real plugin import errors in route loader Bare catch in the prompt-change-indicator dynamic import was swallowing syntax/runtime errors in the plugin file alongside the expected "plugin missing in OSS" case. Detect the missing-module messages explicitly and console.error anything else so a broken cloud plugin no longer disables the readonly route silently. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .../custom-tools/prompt-card/Header.jsx | 16 ++++++++ frontend/src/routes/useMainAppRoutes.js | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/frontend/src/components/custom-tools/prompt-card/Header.jsx b/frontend/src/components/custom-tools/prompt-card/Header.jsx index 30fe75fe54..e5a340f535 100644 --- a/frontend/src/components/custom-tools/prompt-card/Header.jsx +++ b/frontend/src/components/custom-tools/prompt-card/Header.jsx @@ -41,6 +41,16 @@ try { // The component will remain 'undefined' it is not available } +let PromptChangeIndicator; +try { + const mod = await import( + "../../../plugins/prompt-change-indicator/PromptChangeIndicator" + ); + PromptChangeIndicator = mod.PromptChangeIndicator; +} catch { + // Cloud-only feedback loop indicator; stays undefined in OSS builds +} + let LookupMenuItem; try { const mod = await import( @@ -450,6 +460,12 @@ function Header({ )} + {PromptChangeIndicator && ( + + )} {isSimplePromptStudio && PromptRunBtnSps && ( } /> {Manage && } />} + {ReadOnlyReviewPage && ( + } + /> + )} )} From de78b7a826de162d698faaf492ca21949a98d813 Mon Sep 17 00:00:00 2001 From: jimmy Date: Tue, 19 May 2026 17:53:26 +0800 Subject: [PATCH 07/10] Add a dedicated OpenAI-compatible LLM adapter (#1895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add OpenAI-compatible LLM adapter * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address review feedback for custom OpenAI adapter * Fix import formatting after rebase * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address follow-up review comments for OpenAI-compatible adapter * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Refine OpenAI compatible adapter schema naming * Reject empty model string in OpenAICompatibleLLMParameters validate_model previously produced "custom_openai/" for an empty model, surfacing as a confusing LiteLLM error at call time. Match the existing GeminiLLMParameters.validate_model pattern: strip whitespace, raise ValueError on empty input. Co-Authored-By: Claude Opus 4.7 (1M context) * Revert SCHEMA_PATH plumbing; rename schema to custom_openai.json Addresses Ritwik's review feedback. The new BaseAdapter.SCHEMA_PATH class variable and the conditional branch in get_json_schema() are unnecessary: OpenAICompatibleLLMAdapter.get_provider() returns "custom_openai", and the default path resolution already builds …/llm1/static/{get_provider()}.json. Renaming the schema file lets the default lookup find it and keeps the base class untouched, which is the convention every other adapter follows. - Rename openai_compatible.json -> custom_openai.json - Drop SCHEMA_PATH class var and the if-None branch from BaseAdapter - Drop SCHEMA_PATH override (and unused os/ClassVar imports) from OpenAICompatibleLLMAdapter - Update test_openai_compatible_schema_is_loadable to read schema via get_json_schema() instead of touching SCHEMA_PATH directly --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Hari John Kuriakose Co-authored-by: Chandrasekharan M Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Athul Co-authored-by: Athul <89829560+athul-rs@users.noreply.github.com> Co-authored-by: vishnuszipstack <117254672+vishnuszipstack@users.noreply.github.com> --- README.md | 7 +- .../icons/adapter-icons/OpenAICompatible.png | Bin 0 -> 136026 bytes .../sdk1/src/unstract/sdk1/adapters/base1.py | 26 +++ .../unstract/sdk1/adapters/llm1/__init__.py | 2 + .../sdk1/adapters/llm1/openai_compatible.py | 46 +++++ .../adapters/llm1/static/custom_openai.json | 60 ++++++ .../tests/test_openai_compatible_adapter.py | 172 ++++++++++++++++++ 7 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 frontend/public/icons/adapter-icons/OpenAICompatible.png create mode 100644 unstract/sdk1/src/unstract/sdk1/adapters/llm1/openai_compatible.py create mode 100644 unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/custom_openai.json create mode 100644 unstract/sdk1/tests/test_openai_compatible_adapter.py diff --git a/README.md b/README.md index a3ab108c6b..9e282b3e10 100644 --- a/README.md +++ b/README.md @@ -176,9 +176,10 @@ Also see [architecture](docs/ARCHITECTURE.md). | Provider | Status | Provider | Status | |----------|--------|----------|--------| | OpenAI | ✅ | Azure OpenAI | ✅ | -| Anthropic Claude | ✅ | Google Gemini | ✅ | -| AWS Bedrock | ✅ | Mistral AI | ✅ | -| Ollama (local) | ✅ | Anyscale | ✅ | +| OpenAI Compatible | ✅ | Anthropic Claude | ✅ | +| AWS Bedrock | ✅ | Google Gemini | ✅ | +| Ollama (local) | ✅ | Mistral AI | ✅ | +| Anyscale | ✅ | | | ### Vector Databases diff --git a/frontend/public/icons/adapter-icons/OpenAICompatible.png b/frontend/public/icons/adapter-icons/OpenAICompatible.png new file mode 100644 index 0000000000000000000000000000000000000000..ec23189d9ab27571e8f25c030af6b9793e02a8c0 GIT binary patch literal 136026 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�C5Pe;GlpG z8uh^{Gm$mPUxmiGxdYD@O2<{BjBz$ST|YOE5@IerD`!yX{IF6=h=PefXJ9;Ccl`~A z9dYa>KmN%~b*}VXC9{hLb!{e(w6Q6rX>nrB2U&^>qG?TBYT?S;>K)?Rz=u^C52 zTBHH0tS2Vwy>s`6OJM30X&}%+6ZRL(i3&9?VidXc6|_0@TtHo)swd=QsO^*31?p(x z9*;Z(nppM*hpRxODjZr?aRm@x&Z#+5IiX_9?%y&W;xH2-+o~C} zu2(QZXi>%J1@4uEM#|em)g_xpj;b-a(PK$6lna_py7)V0(+H!ONzN=Ib7oC`Cnn!^ z;`{&S2S4P+9@;E2J_C^af@AcBIq1!G>~L^c7s zV`ZU+m!>5#bH+Zwq$;~RXyds#EV7NOVV^qvOgyXcHTp z`d68Cq!xbqI^a~9fXmkZ(Q)5|9r=L&pq#aNU)A1F5Dda(1-lltBuD{{N4}bhq?|T17uDJ3l+|9|^Qxu6vR3(vmWjjr^cbIF< zs2m`JHZ9J&b?LtMx+i|#09U#|6_@GB28t#&sx^K&XY)ZO@-j)`G@^6ccYkMSk<7NU zb8%%R93c@v<0k-KlrT2+b&S%E-Ji}g@ZetUV?@jD?>-WF-(hLAPXNH80Y1(1qaBof zkoZ#Qw0{lQ{cpI8-Mc{%H)QybLoKG@;`2}49+mmX7|DDU`w3pYUMxY!W)(Vvl0eu3 zW5Mh;=XH7cZ6|)Wzx1^?zx_Sm`QHEF3x*v4LcQ)4+16I=)&RP68+~7Q9up|lGN6b(9}5Le zPk;NO?;rcBKYzkw9(mj`FTDF*?zkD9jf)061_q#}CIgV7+@zQ{4GGN>JOqZ)?aV{x zfX@yiMF#-O)43tnWvDQ8v24b<(5ktzD8(SA-h_}t-EL&mpWLT3GU6z+ELq7OkHn&| z-W_`K+ZSH+sn2}@_nc&*szN{qgUub_aHfB&TkV58-|+)jIIiFx7-Atzs)+54f#Vf7 zBL_C2@H{D(KA!wXhaB{v`{C(3rzyE_Du##*<(2{KN6%Jopf7`L}rV zA9Z6lYz?6sQ7v$P(1|i2SsgGYT1?z`0M2Q)6J2ZnLLaW zjzfnod8c2~vQdg0{Nk%TW{H$@{}aLoscU4QhHPygpT zPx|Rke}>G{l{W&QAUzXb@`=}ObbCZ;tkr;&AZr>Rhi}(*ST~0i=>E@N|Kle=bH*!P z`of1jsXRfirCT&4%I@ZA8SJLDIpH7Pw9C#r+Fd@;(;%al z%}@zPOa*ofiFIkIxXCohM-VLHhO>VB8YPaD>-emn6Qr|bVsWDE-PIWf5Q$B5GK_w! zm16`?86Rk-?9xvO(mh?Loc7tTp8Jg#9P!MfUho{OEJW<=m_Rvrkr@dDL7H?qP##*z zFpi;+>3Ghx)MC{Yty7*jAuHmgArb97Lc1kynzPuyB+j5z2Br0fk{>3bJl{L@Q+fd@@Z=3E1TiB$xfuj2NeM?d1= zfB&cd%d@|r1Xz(-tu0bbtH^@A1{0dj>pHIcEr&qzVBq* zKuK$Mw$TA-9=FD@)K-@F^O46f7}$xHC-C!X*WYl%zrOh$hrjTpr+@xSxHlR}NX@t# zysLL&bw2fq`1nL&OQgX4D&aTHq$}7Q{+Ml#=SGYxOks?AMoaX{{OIAfF*8J zQ6)))$pV44JVQru4HuIo`(Q4}E(C&acrgtVc*us#o4#6yV%3?r2)M|shb1=u{jDdS z|LyPcHJ>?Ndxo64V3bYB%{CB6ez~x%Ah(S%A|nQKhz>v`PFE{9p;6XtA~G6i;1YE( zo?*tg!p{}F{57w~vjTVq5WhD7ZJk4WGaF!0#Ry~*v>NZZ4F&-mL)(5^r1yIQA%!@M z2uKs7CZ!)YMV6|nHM7S&!&8~)BAd9;u3Nb5qH}3VRGfhK3zI*}jf8_ihlh5hmz-*$ zpkrZ`xHqsWnSQt?1A~W>@k$${vqX-EBl4NDW_ac#p<>&33-kJOKo}XgMcGY?G)%(5 z0DRoILQjgRUJrbrvb;ct#AIqSoP>I$BR6G@@ikk`wqm)%Anu9}5$tyBm!Ohs*Q zYrrl$@BEx+JoT6tJ!j87cSn^+I$3t^GH&+|e2}U1Gj`eTT?Qa*Ax1G0`UQt8{7zQnsh9PI>x(XNN4r6pUm@S;_rhfs&-)TNvTF#$qHLYS{gh!~NX_Jg4g z94$%9_{yjIzBfGnV=N1B*iUMnCMRh5LDzqVQosyc@vI>4T`5VDpb7UT;$1RtryH+~ z6HHoAuDaw1tZ~4z2%Im$Q*nVt=Ny5DDC^H8<4M0-Ld;RbOf=Nn5(o7>mzFXAcdw3z zl>Y4JmtFevUrzg*7df~fwKhc(wyoJMRF}6bExqJjh zrwC3J@pUGI#z*cv!+~I+*2gNS(&~6bghSHS5>oCLc(E;K8Ys&#B`)uwY4-%`yCYI_GSZ0nrj@1Ql+=!EfWH4R1a1q%WLx z_G^wm`U#JH6!ti*6NjCpsdH)Vv+TGeQUIxUE_5&p*|mU!gO&6F4b(MjTXui)7r$sP zp#S_>dz}f5)AC|L0*u8ID#1dB4gmDP9VH}93TQMXghqRCla_XhgtUO9-yFud;kumM zWt$p=@_8j6tTCtTo4LIRC_P<*;XH~;5y3Thkn*5(%4vapd9kjxG7xL(F=gR^T){iZ zlyI#2?)NYL?QegJ>(RCOKzT?}@G#Ut55E6%p8h2HrG!X952>iBR1LYKW;cAm%regUBXU`M%N#Az9sEB8$=!ig~6`b4`30;)#JvfA-5)y!I~- zd(zim^NM3`z3(k?JFEvNmjI>Eyk1Jvw-rhej;X4=73>1B2cZ(P=ubsJBD=<62Ctq& zSPA3->rBNc(@0| zqKkh-uKoES&_XU@EdoO0^vv|jjye*b;czu6Y8^+l`?@XBw>|1|A*}U#A2JM64Lpm6 zeFkTL7yszT_(|b2zVsEGUh$C?y1>%&f7Ut@!o)=rHKkS7wCb|Oc_#gcmwYP1B367W z9Zr%67i$IcXBiaa)Qpd@VZcBM1*un(h>mp~>!b*@7SS`oAyu_Fw$zEC1+m zhu}=J^Xx4~9-$6WCB7q|_BM4Kq>!enAEN6BSLkQ&==3vdI{@^<9HiZT0I&3aI|w*d zaC$}{UDnfRRCVn8n6mMkx42>jEcCynp^8>ia)#gOc;I~xeDDMAyE3!C@S+*3L?D%S zQBEy#lAb6ukxoqDrkD4A@MG_M@5#Tp?l(Bq#ZSlBQ)|91>-{+N9&veVDHTjH=i@5n zx|x}M_ulI^x4!l5Z@=H}yWRo|l;TEAeB58Z{@1^`{`#N&;+H?iildib@tfbL>&qT?8VOs-fg7k(M7~%M-}YPX(;{iZ;4R@b9Edy8Fm#^f$1{m78?G2X!z4}V;B&H9V}=Z9ZE|AKFP^V>KcK#eY55vIV(J`#3Q zL_cf{Pl-g;u~wE+TwdZ|N|<=pNhe?Olb`XF z97?vM@M-j(Rg3t@fQxdKHLM>fs4p5qYC8Z#95o%ufOhw-{J$fnLZ=CT6)MeZr<5rw_P9h;0My3U?dQOG4v*Z88S2q z&FyOuy%nm;4&=d+VPA>Hs+{;=c+tftyycx=I{REOg192kyAz&45k6qq}wMJZ`p0xzLXz&Fp`>{cyB+##5iT{@2%@ z^Yw3j?9@+x{lCA7Z?NrR#eBb^cg1v3EyN+>WC#?rEeh~(_JHTbp8dj?{qx_x@sS5V zILVt&lg~(zg;(w@m6&xJMk#^*FetGqXS9=uR~SZscQR{A^DqEaaOeqD^UlIj+W{c7 z8KpL2z7TFDD4UE5!HNnWFt^FUtire2borH6;+xxP{A3l)vQ^bwBi4Vv=iTqR?>>8B z2~4zC5}UK`kkxyt!+4C%VN|XrQ$Mb#y!AaF_|QjBz2Sx%u(BK6hFJ`2{a6`S&Lde< z#S3s~!i{a1RpmtL(npu1`Jmafd$s z(dT^qg15Zur1S8*$8_73tn?mOQ3d{8-<0BW3W>E7c++M45W?)0S6%heSH13^|K>08 z3p15tfS7w*2X~V#Y)7#Kz1GUB?Ev5hZPHQH)ap#eUINiikafl^a-Vq%*gjVTXCO%! zpFbAt$0aT&)^Msiy6p0+FoR4_4Ojx2qQ;$Q_dejB_$6&Q0N8T~N<-CcYoiMRUlF$1 zjLkK;F$n@vngTY)i6tLT3ER4 zez!UHsOLZXutTvB8(bBqPEh6?dV57otZ+-%BOdmk2S4CGr+xNJ{Jg=%Ke`0JJ}*ZI zt81+tGbymx$w)be<7;03rj4669{!A{6a!%pYaAf1Qiaa&g0m%? zlvVP-!fqrQ=|6ezaM*SLsDU)O5dg3g?M%{J=ZKgJ05fQ@maBKL?E~d7hZYQ%loAb4 ze6`DaeT@;mg~NGbQKKt0m>uqMw>!BrP0Pb>hlZu?ghG*_Rvmvi4TSED#tiVS3%`fu z`_KC7dAR$0hgm$%)29!Sr zl-(EvTpD`jVTT^_$cKIKqo=(0gCDv4@+hsegj1f!TxcrZr^Dli)T08juf zhzt8!GAIk?saOEzy)9&eQDBmKXqN!87X>H>`k01rSpYYl<3s*n^^dv9DxkO-xP;R06e@u!*IdT1$mi!@}*q4hJ&gpU|{Oho_~;I>Ax zZ%(2Rh)pD_d80P`lwHTM6B)rH*7Zo~(Kud-ApFKnIp@dHRGo{QYtAB0b|*0t;XT&z zb$Zl$5@gFdpo;sZBM6reN-oimDK66TZt4jUH2!4G#Jycm0tnMAdVw~c_0?I0>e{t4 z_&)Z$Z+_<#{9^Z6=j^-p9)~>qAU8p42ecC}PyXgN*C8j|=h3hgp|#K~Yzr;`;qJn{ z_N3EK`Wm_Xswae#N_&1804;RmO1X2u zJgoe&Q&0bocbxRUKl?eJ>E+qq4Gm7A$8zRUP-+>Ss1Ln{E73rq8;!PN`Gh;&asQVd zb>vf?_*ksnjr;u3Z7EwZggnz^Mim{nfQlb*Ok#CzCvLajt^eh3|MF>1`J=bI>wRB4 z_v^Tel+G>aW#Yod$L50e08UdxUY7v;F~GGF1oqLzEen7C#=qNh&)pty-+RmGMIhnD zC@}#HI*rPRqO|*?U8qSmGC5%|(c!TwJVjs}$%aP=K~y>yOc2)W+?*eQOj4>Be((Dyyye|r#x}#Z*oAS9>%7_Yzj_TF^JP&#?{N~ zZn*w6uY2SB-}N8+-13%qumQ2uM@ZZ5KUDPr|NZ$d-uSow^41gnULI6T z;rdECGX@2YI>WPsOlD2o*T{4OhLCiT|4w%lI zb3_TE2$DnuMLo}W=b05n5mbVLia9F=jF=-vR76lQA_5A#Y~uZ&s_O3Pp6N;NF6w>% zu)Ncu>Z#CSW_r4NvVf2+q=%!66)6o}0IV#hX87cL($J{X4G3kLUZXLr^+?N{1go?P zj)8FiAhzi=FOl2fIQER`QqMm4=u@me!V5{v=V^vJKJ>`&8^n~Bth(wd=qcjR>gwyz zxn$GFrc!`7Ve2ef>>4W}PO_7pxgTwq7)NGI#Ih2;#Z$|Jk3Dtc=sUieH4F26r1y6V zHU`I&4Ct`_mb3^1lk=M1nyt6m;>L{SNTos`(+mTS}ZliZjm0OmE4auPkKp4l6hBG2@U@@_$Lx?C4Va}S6U7awZcpy8!htiW2mZIV*mu9;A z!;|=rKm9y<>_mK5_K!u2n>8&jtDx1Va@CYhdBwKN#|R)-V@w&(yw>&BOB=5=UWb&) z0Fsw6Yht{BtO%gX0^-);Fu1OM^lXH@_osb)^@!0}pactBV$27gbuM%YEE&_40oXKh zZ3blF`MpDj_I*z3(Y?!2_~JZ?PvjO>kOX2uiX&A&BdE5A%cia}Anh2vie2p0K$VpVW5k)6K z1%*LA3StR74K05K&DNWOQIfR+7>v>yXfGL3ozd{KhHf}ggAughMFBO4SxclBarpYm zgh>ye_s6C#*k%|5@s!J+7hfTWjjdN%d8OEH14BS{xB%3Fi}^IXb52s2+hz|AivX7nYqO{5qk=E-*L^}y#59Y>2% zS9JI9fAr~9*WWVrjW_W!j^9YD0=2sde6t2UGn!?nA=-+89xsY@i?(wl9H)h(Luk>y z2+4{QWeM==5>-vvPElgl|ka6(`TaU(^L9J>4w`~D=6+o<5B%BG8L z17MOI=#}D0#C<;QNihxawmT+a5<1=WlWS3K^hhcmDQDNt+{5O zle-^#_(3V3P|xJBqKIIFxhityNJThBYn#qt?1 zNkA0^$qloZ1XSrJUUuzG7@MYh zQ?~3g|%Hj{xWyemAQdLAl8L%w%#d z$`FZoBa~P~)M!!!uhD@(P_#S^Ojp%dJS8z)ka-jd^M+-`UjV|Ce?dHtp?`sTSwv#; z$)d`|*WPs7*gGa+?-v^LQC0CVao&a(TM3Ih4oAS z@sC~3Jgx8c+ir>1@=L0gU|fzT*9MH)Lk68$+?XE)G)mtr)KLs!rr89gCHwBV+m739 zeaGGR-#Y%zdGi)u*Zx@Xame^ci8)0lWjCq%Q`i0(1O?rM@8%k2=wAR@Jv zxfB+Ka9zYh)U=PMUw6~3FTV0d89fKk1b?<%<1%Ru>(ca)eEua=Ubs}V!Mbao*05?&zR^m>oAv$J+~7IP3Al_=@QIwHYSH?lyBk=dra;O(kcjgjmKTp zh+A$SdFvfmuS=uS#6^E(0M4;?AcN+K(#@;LA;#!*70Ck@0}+$b$pGtwP&%PW5(_cp zip+OngIV!F4AE%``t0L%(uk1$%bhv1W#*+Ws)WXT=KlKImX@pHaTvx&a`lf$D)lo<}D?kBvOPoG}wG z)zSN_R;~+xMXFLT>NPYFIHLp&p-GeSgAd#fQ#?Cux)FAE6YoJi91uhcEBFJs{Drk7 z$>fmmAxY!JjFPL^Al*XIxilpe60f_8F|h31bs zVX3HWWG1CJ9AXO&nWI1=MMEsK9v&xLBn4h6t-$pr>Dj5i1XYCg zdQ&QpTMQnx80bNgRDO_ULSRH!M;lXfg*NF3fE|0+U}ylPKw7`zlj&cM7(M>k=U>Jk zJGSqaET*)C*_;`}$=qxbY6)GQ>gqaezUh#Gr|z@IuJ8v2Ol%2cJ`N;d-#5HkJdcnP_aZbh(3Yw`kG^mobS0@ukQsM-{XA%_A464UNLv2*9`#*MnR_rSAeeS^0w z^nzWOi~23CtXeYp=_#GJ-3oSvZ0Gx#q~;Ha^u|Riz_utYY@J|Cf^Osuu4Ew^8v%;J zCm{kmijJ(77dDFy++IwJA+G`SVLqR@&*|uxZd9jPw#RZ!;7OnZ>4+VoqmsypY65+PI1L;(O?L4k#;0m>gvs5>_x{ zT=;Dx;AD)7bX>hd@19+;thaURR=A5qU*rcAf7$##y0m;BPs7rb#GHarzc$`*y^+KJ zd&+>JKmYP;%9CK4hIrt7Vd|@c=l5yVI`S$wvtm&~q~s}e4{c5at6fI5NpnIppD=8k za6HQ4OF@HnP<%ALw4^&bAH65dl@ukJ_|Jbq zPnyILmxor5jn%Nit9zfpm_&hgfu@4lb*@u#@+2r+CQeL4TTX&CITdQq^n?%UR0>~g2o>C zy}BR&@+)t=`R@BDAl6DbE*B`KHN5x!hm}=Tc=eFSt70ym60Op5U64x+m(_~V$ZJE8 z@^FhBpR~rPLug5{zOoE%)V+Xg+(r+39Ho-@#AD`-E;I?SP*TyfeE$4}|GebNC!d*u z(FrpQ&RpVskJbTZG6|1#1jNx$B6gJAO(AAiVK=`KVr*Wrjg}Oci!ox1No<%V z_x^_;V+e@Gd{jRnKo&z|1&te70>0eRs%5L5-MaMe-Lw5_{;&0>sDjIO;+4Cx6wi;7 zxiBKocy-#cRg0m6`tWIN5C$qSS zNN!m34+<1$8BK}THb=9Hm6hG4NqWTR1v#0X0OIsSdf>YN(-%smi<6Orzy0jRf0db<)YnAT^q z0@2nJRVV?5F^mynu>s&fmir>i>YVZQEPNW_$){gXpGJTS;(AiA(^7Oqwx}g4+SHVe z%~dhGYqy;{pFL#Ic3W?blz8df#rqy>iqy;2u z){66*+xut4fW4Sn`{@@m`VT$ttFOPocqmNDU2B-fktivFH|ZuIh&iMtg8U?sR9_nj z)RhUUQ7niji7=|f21tom1nK=>e(lZcZoKu~4?e;R70jKLZiN-m+UPS$i3N*QAua>v zqoc2`sa~P&^8I@E?A5&smi6L|PHOKT2NlbUZRm_#iYFpG zcm0WRR8}<_Mwj&h5a*zX`95`+NI!e=W#6zBcNY-|F?t!qATK5qut><}w8MV(($ZPq zeAoZX^JjiNTV^Ht$r4goYM_KoX;LZC^v^bh8%|RfcnKufZI0mhPS%l3iIhXU3H(H& z4yyFNjy{fCy8a)2{Au{jWAA_X39M9 zd_cSuAIm+xU$4!uthcrn%XhKt*}!B77(fx3s+prh27D=pEQGN%A!n%9s97}CkSJU# zTa*pHBi+2&-h1r&+FS3*1wfO;oE_QhE9`S#*5teIeppml#XC4@3f()z0O@4L(2-)! zT3!q<%v27FA@C=MP%}B#=20(fISD`9k}`nsrAE^cpI^Jg)N)>JXfrD<_@r2AuvmnJ zML=_E`DV$YS9NB^h_hOAV9_Wc(tFr1g+g)^9j#l{kBy9Cn+7Q$Ga!}}p%N_$(LIm@ zSroz~5d5pR&8Jj`!@d7cKhGU-=6PSu{F?7oZTZ>4AzZspiU44}Asj2d6$KOtoWT1H zAZp>6U|>)L(n%=e&w<4)1O+LKhA1qZE)>-Jy1?sN8qUZLEjVOHiY%oJS;25f#jM%t z>Y95WdhDiK?)Y}j9CQTOA6r|ZakZu$jR&6E>yQKXf^V_1 z3;1M3X;6itjFN(TSgB0x4be-aQW&8oa2eJL2hIv3fv&zD3qhUqASSrHpwywz5uK0i zB9h;uLJMcI4T(@<)QO5V5d_^nY6wxoFg)0llwf&qo7Sx`k`vNjYnN~)2&nYyym|N} zM(b8B-~`pQtd<+c4}X+4@!w|{mSkH%h9Z`-Gb}Jo3Qtmaqktr@f*mH1L-c ziG*mJ!(8c1labPNh&YmJ3%Qc6%@G+E7C_?g$!hGL5Z*0BD`=boMUyv_fJd++inx_^ z34TNqbRg8m6IXx0W%i~tA)7=Q{%~E=$~D}?VWP2Aaiv56L<+)L-tAK?kCoI^*U%+E z`@v-4&Sl}kg@cD(Fzv%n>E?wpjFSwNFOZFjIH4^QUYYyyBK z=}Zg;%@!j(@PQ8D3_wih02IDYkXVIEP%c4*j);JGbm%mrH2#1TR5O4;ZPX=<_FOT1 zl*YKf8)?`2Ub&4Ur=6tM1Q)m2wLd+@+DSMPv&5Y?G=iIixnGFp;8 z<7SLplcr!%#qX5L7q;VZZhoje*5jS2iSaFf1vPR z71GA(^IxS$n%oUGSf}u0+-^u|%(yeHtHb_I>2&!i{#K6jRzMt?$I71o#(Y1XXbjkn*&SG}-|1ySvrFllmJxNN&rm``}~ zR8>>cvBPRZPV0BT%qA3qx}A%G*Bhcfvj+Y?@r zK;)SVfT*VhBd6i|=6nc9(iD!%)B7hwX(Kb8F>F0|-ftIQb{z&TaqFt@TU~cWk#v5j zr{iR$ofw2$a*X)n#{Y;z4?K6sz_r#`9bo4EG<3rrmIDYv!Jn2FrihQLcZ^_6^%fDN z5=AAU$S}Wt{GIpWo4xpo3tpVyQ?tgjD1E*Z%VH}SV8HX(zyKrZ#~pR(X{YvDf4#LZ zxQ`)Gys-#~VnR`412;wUVnB*cCRtvSibl-Cn8m)8S6Z=5KAK@8hd2?9x8j$%a~;+Y zubr1C+(=8ci$-C}=DD!C;dpLUZ~?gX#<3qy z|Dq+2Su=k&RboYfU8{u~|LP?jR%Kku2EvE_oAPtD^u%nrfX#Y4_IU*QjSXy7V<96E&IqlS4cH9n@E?J`HDda%0 zNG*k|pheKQ!N5^DagA~&kP{#id5ab;@B#o{RZ`N0fg-R8_a5^XEX*XyCQ7@ZOxMCj z`_sdY3vx;q(*h6XWKSTM3&65KP7qpL)?c6`Zd#vy{*`+kdaQ+7pp-!eizMjftVFqA z$CVj({WZ09`|Y#W1?QZx{yJ;IDWM)Yxk4xBrn{K8gC?QDH5@B(i1H&}0-z9)A-@{s-U<{3{Rf|ctbGKcv8yQFdV zk65&U2?Ur!mf3Wd5)*+IEk>aMUoaAoOx;_yuXi(Bl9v#c8xs_7g0(($!*17eAE7S| zvf@gS$>W~@6q;LdJ`r}B(N!mU%-30Y&k<}NKp-z54?;eU*fi|!`RlyjuDEVweO)a! z_z(J+SdJiry%zig87hQ{vo$NMu)^sBPVLp>IE?s*x_(8KNpWK_s9B;VECgUjK}w%T z0$nllr<@~?jbIUA{&=mc(rZYw*5{y z=9C`Cue$Pz82vZBzlZ1Von$O8PEF&%;@Fe9S~PEgZ(iWdIyP!mM;>`7ER+i`mH>ZN zLlgTkrG=>_SwXe>%kFGcDo#X*9noB-r4|bo;KT!lgcoOlYMjL~3!i9`YK`tT8YT?f z7mPpfji#E(XLVO$axPI{~;+vTW__a4vAi(RK;gC_qPloGJqZ zF$kFMX5%$Zm{dyDsc}SQM{)eA*s>~_l`gwB5lU*JJ{6fTrtl&lL4x5>suQ$KD>69& zaVR7d@iwO;0FVTRaJ^|^noykft^It)*Apf^L`#IIgXZBJo}VF7LJU@v^$e8d-kqbU zoLH%bL5Wj)^%>l+7k^$VptBH6Fk*_yAv5&|tV#rN44MxHr1}G@2>K{2S%Cxz#mOlx z#ZJG&F1T#*;>EnRgG1*;qTrNbn-C_K0uUmb8{cigtKDt3-2C+ZefHdKCp@=>x_*}> z3~NJ~uubH+1{r_a``P@r1){d2urVo5fUFGYZD$0jDr9H6Y%ZzbDQfi7SjJfFeUA(5 z#%y)moZ@4aE8-eh3V1{-O2DmBaUOs^3J@n-xmYa{=n7bhN7)K779uqx3=qVFP$~!! z2ze&EV(^qeEw`w90$N}_dhA53S?8T^*-1(qaO1RNkyA}K1VYDx9R8*FgN`NRIP zOJ}S&%~1Tp=!z3It9L-QCDSF2agij0)a6a2Hkn$OJ_pZ`n7WD)vzfDJW38OJYLad_ zR5C(h0whz(#fhePE@|I>wLT~H=-Kt?R;^lARaMsE8~0K|R>3Ql7*IDkOpACU8sN%W zCNVLffjS3X2ttmwmUo@>OcAPfF}M+aLkEsjp=cavxe*LX@TYo7mvv&XUBBoRID~Ei zQ_Vu(6;D+t!LsJy6NX;H@vaif)&n^{|6;Tx&hLVWuA#abkK`K0Eq<7UwL+M&mjHsI z#qBc{upnxr0FyWR$g)a1YNmjRXE88>CHVMq=@U;+kr$W|dJqQUO^-xms&FjxIEcLg zs%oka+JC<*FFtRz)mFh?H29-c+CFEBILrvbCJhnMDglgGTZI>KRJPDX$+flevRayB zW3Lu1Fq?ZxWmQ-Uf)OaX+Q1vhgGHzz7}Ua}(v+88ee}tv>5@rS8z)#oy-F38h+RDK z34!j%A3Lz`No%j!v8JXPdrG4olImf50s*LpPGt^qR0s}9u0o*0mIKDN0DxRY2ZUFY zS6Dj8-21JV~m z`H6)KwQ1K1_Nd&k;wyOA>u70wr{XFaR8FLu`boPO)xy5II;kdT{;9U8<{wl}M-Ysq zT`Q~)vO3uuaU7zHP?Z|r1fyq8a|q4dV22o6qH?Io_)AvP!hDR8DpRO~wr(*w73|3l z0TTR&$`q#u?1Dp5yLb8qlh(?AhS%sYr6&#}S_(-FAgKnUCV^bk4KR$%$cADjM*^aS zXo3{$nMK_gAPWX3n?vj}_~`Z#wmv|>F@EBG_zHROr5BHz!Gu(cRRygSqQGPcY&}-j zyJwG!|2dSKJ(?~xA9@9=%+Toi3E>}|(%P)W?g-Z&7C369@<_rshYe)w+Ak9dNFw)miN z`*y3Wwbq(zc3izf`*y8bv_O4erzZLYTRqVwf@MTCrdwUQZQ^YI57^ouK?5ebvGanMOiRBCg2wjTu|FaZ!VyFGF?JVOPQ;7kK=(tUw6GWZ=I ztiZkncp?euXu*jSv@4LIgZivo#ci-~_89{)AsrgppeAZs|rAP z9vsot;;@3`QGhEAZMl&!s%z8o@>kw?=dJhNHyip#sQkJaiP~=_0RTFUv5-N1eRb`6 z>#RNS)RPWBWPfbSk#~}hgP6}*u0dK&oZ%-ti{SeA*1OXlpFHJ_cizJ`*Hx8Er1KTC zL4&80)~#EvyVjb!@7npm{r1{ylZ@~GwIo}QQDMW!(d|28!Nd`m@w%<>{i$+yy=^9awhf3*wO;-^@a}YH*`>6XlM8S zqO5X=I^{MXe?Oo_a4XiAOkaNZ4Va23{6^bGABx_DA4#z{!Q&V2NZTzHZAAJm! zF#dx<5^UvZbBTtF7FWLi(I@YG@X@%5_Z@M_{->VYZSA$z09mwRSiD+NkYsr!NX#56 zhC8{SOn01B2b3%BxH0&E3S5Vs#_1Z6CL)Awypq!#Lv8?EV3Y4A|F6Zeo00PZ&|pVo z*DP6-;HX+dk?g<@Ye))i|L6Sp^V3saq?N0&hcss^I}olzbS_|fpHoikdgkf=-e34Z zNLHXJ&`B92S;@aF!KT-uHJhC$X)KF=sAtvL=zZ+2Q&1=~vq~8E`1QB>ufP2sdPA!( z1BQTUh1|22uTn$Qk}JO$Dn|cs;C_3b-tUwxJ8g=g01WrLV8zFhRE$Rj7q+!PSO39B zpWQfm{FIlc;-wC@y1*1KZFS&Cz(@p+f{RKkO7XJ#_B-#L{Ok)oyB>RTk1oqEzZ}uT zUeCd8c}uY3b+dCUHaofDVf)&g8x39m7r*>+{!5d4Y`Oi$E3RChFp`~s?fRhzO9h|^ zMx&HnFs?d=q?kacNfra))R-iI;x7QX434kdNy??>IXXCzcvg^6nX6NX4mWPNll|ex zpLz0+$yAlZ)D`Znqw;hLXJe0tefRvwh35>xMJ3t%KqI9U^#mHr=nxJ76Sq-TDzvks z3X2GkY*c3kp`*>M2E3@8uxE=3R%$)rt0sep);B}$B*rHeK~K>k{G+YlAb-~u}Q^W0E(5ktoR~r zFJ4fz5-}}qgmG*6_~hrYYrNEKsjbM&QPH{l@ak{fwb#1*;(wu?XROvDC9oo`9#F-_ ze~<;r1);Fcj>#0E|H-cq0g>Tgk)>bEn2AZQE!B5&zy|n)LusQx1G!kDZV z0Nd$O2LDqYjwnm^glxjf*s^IKefjBUUs_+fpqgfCn?d0e@cT;)l^BBqmz;O@8f&bc zr9vB6Kb*-91t1>J0ND>l*YBN4_o!VyuoR_`4V+ouey`?N5V`(u;0#8xNEwPM;4@X# z*n8us!w)@kP~UaeR-@!$Pm*BzPnW=a2Xp0PA!^a?xfiEieZ#0}AAN#puPtZ_HF?sf zfuscnlT=$W=YthD{vdksl~><+_k(U-j_lv(#8p;a*=Psj2qJ({If8D;mT|z=n?ymK zh(>u-P_{3{Hy)c;l+RnT`07`tJoV)#LwDWlfb};AP`Iq~VE-@El3$7|*>DME_CnSR zfYA&EU!01}*Pv>X*{C83apV8|ORp}jT-1`@WGXB3XiT)YN5uQdUMF?iZ=cfegU4Iy&P?+=m_xxU2IW4w^#LGcP2c0p$v$n2&+pV`AdRp(j{;@OA zLq2ruA~CU|0^=GSlCB?5IU`1mee|j4Frgn)IY~7oJwXS_vb44m5Tr{2+kio&>Y61t zkGbQSDK8Bkc*;?S9*nu|P%qlHyR$e-N z?oa(5opki3Tc5G(K5MjT8!mXaq(yiLw&77!worX*IOeiMXaqOpxB!SH#mfYza|`6) z{b_y|{X_K`Q8e<(%Bq)NdlQ!dx3a~SpsvTm&4^gFpnEL)>x;BH<^^|jC> zIp9O@WiB9yMv5k|E(wVIR$o_-ad7G*NS#0>;&Wpv1(bZKvz!W8UsKbeeTROh^z7E< zaJmMA+h}Oh3-h1yD_#Hm`3pvkn>caO1Ha8*fG=5=H<9)8q+l*B0%{%NGs=;V(o(#6 z`ex3YGtR#7frp+S z{8$ac1xzGZZ-`|nOAC`ec>DElXPvj_{)cY31*b=t4a(mpVB1}sx;6q?oy|16ao7mp z4n-62a$f)vu#&T26?ZaXooi-Bi<1T9$+eVGzQULTZt-T!oQ2UFLUI+1!M2DP20!@b zNVhJ>thFY6*JYU=)HktENYUqf0`*=%ey;3gLOU>5S5s^DBSliR_eF0gkJ*#Wo44qC z+@YuU>m{xWb^U+<8Syb#NDvYwJtE1I04?cRBf|j?W5&?~k4(Plw%fm)@fEs$Ea_6B zjVc*%5=jWpn1cX~2$fg7`o=r&eDLuJM<3R&caM%8+Vg~0p@Qm}{j3Fp(^za|>CBAh zfDhu;qmPX%W*9MPsC3aIQ>>yOvr3y+H2r@5Z-XAb=gnTpaR)CPwBrd!B zhKp_#1mpW$Xyv%YAciC+T49^)K0K=`@p977#>AzkbMyBUjgnzJWYl3@xIQd26-1Aa zzBH~-ut7AKbB@{~cX{Py<#nnQj}wl;ic`Y-?&tRnX4MTDMLQtV}O zP)R`f-UlCFb;IbFroK^LR@$m%3+Z?R-AH|{Wy)Dx5TZ`-OMzF6FCBl^eHfEGt?x<4 z9C1jq=FPG6-{ygas38m*+zQx=z41f0EA?NQ4y=Lf3NXcsO(rT%&5WIW+NgR z!2KGIXu3&(TVr<%DJ{i4S5Rz*Cr1DucdYN(^|%hJt%~ot2OdC3d)?FkjPT+}z$S;l zt1$=49t5ar^as#DmNqWCJBo($QH+Ah)49nP4!gj+ilXAi}yYBh>f?xH2|fb+~whNSj!*(i7P5- zp6n24z_mTN6Ll4*SkxgQ8aNHJ7I_aR_$_EijQR%4<@% zJJkIl>k{!yfo33>@}>en+@i9A1~yZ`(M#8-6i$~6qAd{WFETy5yKEte7NC=Znz3l{ z;?Jgkfkm87MxIFh(t3>kAH4tG#AMnNg@bCXTSR-9~^x=p7^Q=J~JG4WJ^xbrM2Ns#ZkQz5z*)y>D zfzqzdKqcV#102{I4|{-*yYt>TbACXNgWUjK442vVUID*_gB4V40}v%9Xd;f`yr_s}QP zu6_OaZx_sKL2p)Y(3OwRg{EbAM5sOMsfWIq|J%SFcgH?oVW5a4NdSL{GMW;@+BnHv zXGn#R8b&?JN~%~{)DWB4R~gCC=v|>ahTt#*j`{}r%XxTB&54mfPKsyJDHD-L5H_;^ z?)&dCPL&tH(BoEW_g!}CxO)3Y(HauS6+hW29%Yh~$QSq@F3WIji!Bim&e_(bDN&{? zD~2=?%(l3c=J@Zs=k6n}{x^DmxQjR)0`3H?l-QmqMiowubwHE}_&6}$a{NiBU2^5{ zpMU-Z?_-g|b|>N>Q~pAr-bAN}jClzGu>%j#6&O^UFzLRdyY|NV1FTkoZb~4|6iiey zq}yK{C5`D3w!XfitgOqH+dkZ_?{S-KRa0A!VN>+~R2j52*v<^rUH$5m^PYR8vbH9$ z`Y$#ilXz`JCgQ(}2!qH{lOo!Z>*;8aW>nT9p`kcXBvUUGqplXO&k?d2GiPJ_&%oYT z!Z0TsCnn-v?SOsvM0Vr?z)ci@gqayPEXN}Vv%wT@oD*3MT|$5P-b#e(4zu4 zMJMaC2TDZaw@`wesQdTniOpNZSL{H}rXQj60OUyuoMO1VxLn6eznVGgw6o4XdBD*3 zrhSB6Kyb4udthpRCRkhas|wo2JBB{*qmpCQ8Fa>2jsuGJt*)15+3d z>e+9|;BzjX_04y9F@~|gTsR5D*?}5j6L`&NzTn_K;Djx=yZ40NyR5#}5^Qs?RphHc zp>%0U%cjkqoc{5ENAH@y#JWqVP&#ul#ME<2_j$axF~Ec06B58 zk{4+N*aA)p1+lD>bJETJ_B*^$(cOP`W@>z>#F)FX>Bbwcyz)x1oDFF)5m11U+!S?v zB9W$Vj+5UYMPx=3iYyddEZeQJ3VJS2x9U?lDmXF4g@BIc&tgOG5AQwu!Yjx0=zHl^ z*DqYS5MO?_u88a=q1PUhBp2O641y7oBK;H~hC=vK5USmM4?T8V&;FyvOsuS0!qYSw zBu1PR5)>DOg#mprVrfcGaM>cy($0zNue8dz<4)?ab!SWstJgOu3cgU%vZCorUrs;m z(YqI9y9W4_FGg1%PL4Ejl=;oUxvV-p%L!PFkw>BYv~({hmYbnY`;84C5t2U_B7ZD^%+CuI&;TnE=OLt{ zlB>zrq)DnaWHkK;EqsMZVb})1suwDYglaMh7jcjTK1e+TYkkmO!!=bd6$g=*0>RS6{EviU-sMlB*fJD!t z@bVmi*C-UkvSB3B$7q6ksJZjz^ZaHOl22c-6fhc#VU2VR1vybi8w7;0w7HeEfhjDD z2pR~(50l)mOk}w9pjM9FJ^ol2XQc$ncWoNQAz)Zs9@dlh3A~I_T^HXZ-W? zFTP5hD;X7_d_)Rn)lL~>uoQ%erv8kP3gS2kdYTb3#(g~rn3+l!~q~0i$jxzCEGLR!j8)b-e zf|DD8RI4cGp@1@{ajYsd)&-!UR)rlAnCC6iFuBA7CRBsyH2_2w zg$?n`ufJASR>ox7g#Ix0LRfKywkxl+qMI$6M+zHaVje_Xc2YLUc;wkQ5ji2Wa1dB{{JFV}@`06B9y?JQ;Nr5?`e6QQd^5B{g;z*0K*)ewj z0dG}pZRd__-*RNv0gv20d%--c2?+8EC0IW^;oUb@ZPWU!UG|F>WI)+~kXt$p01{Sm znH;L1Uj42^XG&=Ny}xL!5MFh?Y)XM_+!(cqcRrYeW70SeP( z+hF(rBrP?RU4ZP2gs`O}meb?)Z<{u)wH4^A_y%3sB4uEDbBi+ppL?h{Qcj07YWay6 z@wvtIrwT8C<$eH9^s*)m>B?AJt7vMdZ_>IDto`2x$s8sCInc&1B!UUPH{CM+(I=n9 zLJfNJOoc-=(!O)l968Rgv#_)t->bpL-g}&I^wEbMxauk^(KWSD^3yNB-gf()cieS9 zzL9{XWZES5jO(5TsA0?$MEXT3;4<*l*RuzobJ0VOKaGu*cHDj&q|>7Dg#Mb444QaA zqJ-&ZBsA9&;q(87iQ#$Paxs<~89=YBtKDMN_O~9@z3+o}%v`vj8Sk5I6m4E!e*Mc+ zI<#J{>z19JZC9AWkZBo_1f3B%v(-qYre$-ny_t;$VT{XxNhrFnh{)#zJC~HO6;T|X zBo9`+9!y+Xj?RFNhGtt^ zoAOkPA<<^EMSJW4x7OJEE3lif$=7f5=3|dvJebCEhB92|qe3s&rWJ}5tGA&iIi#W~ zT4PXb#udO}UN{8AI_B0iqyhwA3!(1|x(DGpnud+UbFQMZf?4$N0M+xcRm_e);(qbp7(>7Q+v^oYw$l15W61s+Ux+w9-mFjz8w) z6T7UoDt7S4zAH#3ctr6fxc&Cp^?=vp)Cfh5(*b&aJy7gqZ`Vuwx%wMW(a%%?6 z4K;4Pu$Pj+J~QD%m-6EH5qH9eFe*W z8cLcb;#R1ANQu}#r$zG?M;>>`fZjbf+F%_Ziyrl9a0!$SS5v8C zC9UEQJ+MT*A><=_H9zTj2|<4viB$VB?atG2)8sO~4-f5%UM zEJV%0u2U?HwvAg{Q+M9ek4!wSSGyLi1tx?LilWs~iqGFFja3P8*-_geQOB_@vF6w~ zROdNPzKh(7AW1z`+dOU<2+AC!hYFGM$hpwgWeUesl?^9bao{KnoaE2SrC^E(i#kN9 z(L%`L6s%qy-@k-5q&9g+0a; zs$yv}vgb>0p7kn?HOo<(YsYti^lY`lWmAzT3nxBX79!V)Xvh%=}i<%_cm2+))SLKjD@$2J~*#suikP zOlv8^0b-6;I|{&BVLQ2RzyIN!3okpl|Ik<8dba}ig!=V+l*95yQV@L#J@YwVDPXWX zh?smOZ+4R;={$INvv%!A9(LS{&6?HHzO%^SD`=slX<7MpiJ&azW}HzD}9ZqjPPIOY*pcPy$|e_lxTWDkr2YoxNQ-mpaIFB-EH{s zk=hJf$E5YdmIC0&R6G_-ILLP@s`wOEOnp*qh3qhYghsQT8tF(k29m8K^&Ox^n<FPs29sKF8WvImx&W#Pd5>avlni5`3gBdtj2gw@qdK9La&K>NLdafOh8T)q1fv+y z8X_c4Cucfm;*A;rg=O%UFAtd{#S_7HZGO5sM49|8ns`C}9K2G-wfg=?Cm(ZSzZ*x5 ztz5hqUv7zzY7I?>li-OU34iE%aOb|p>K!h<;G9Y0M;&(1K5!ip4NPi)htaDy*=YTn zuDx{R@GCaiWCIK$QwK^%4xAK0J|rq!zKv;y89{}m4?XtOaVPd4dFvfZs+Z8)>Ffup zH~mRJPn8|xOH1h?!0{X52~8L|Jk-{vHAZam9o4lp2X5SH=&t)>p#V6=T>L^{)AEWN z-+1Y*A7&Tb41LZ^Q7>Y73jH$3CanX_O-v{0bCTrr3qZV`@i|_J@#I+)b!u9`phG+e z=@pxnh_+n7r~W)uIm~Gp5Qqg4bq0D!G|ZC}xA2n4lJc}3Ni_aEv}W}Vz@MKdl?=(J z32nKPkHfDI?Typ4;@uBFK6UW9XZ`CxvuAyS^|AbBh028foUS^j#|tG(YO0r8uFa5v zeIL4e?8!Zj#{y=2@Y%0QS}KN3FoJZ@zPsOZ`>1n<4qS<*>Q{633{EJPQ#V(fvQ|#M zEVu%rbS+vmTlmNC|GnnM?x&po!b`7Va13vBUGn4@DT%kB8*i}w83TLc$%bZjKq%jj zvjN%)eKv4NP-%23#m07duAvHMNli`P?RV|cX`8CLYS=@o#JH3(-J+_d_CL=(_ItIp zPpXmqCk9{~DfL$#e4Wm$K>7tB5lWS5B8|!P5f|+(Fe=_4WF#d8xVx5mBM|5!Fqsc5 zf~Qa&2xuNrp2TwhLJS2=`t~t8D6x^^Ic>;@hIXbCCQ7_wqOZ6##^Li$3NCg7RWoLU zF#q!tdU?$2`SHhJ{&nd!Jx(3`;w!IXSef>i_VFjZPCIMh(DOf?{sr!ubKN+jy|irEV%Xq*_fNb9pD2Kq{B9YY zu+m>OmZ)#FFF;p;JAjCyIzRSIRxB-Cxc9-^v|ppD4&O&J<`yvA_wKi|Z+z{!SY|6D z>>*F{wy1qUw;DMVc{*S!8i%=|5v><1p(9D487=_v1(^iw$!n;o9)6RQOG$9qe|QJ) zv}^;aY_R-j1p@=A9@6W_ASGMKk>f4T6?y+WhhZj$EN-t2%YDS18SzHXHx$YwygNnP5l} zBJ=4;_GJ{3kt{*J>trXE^)i&<0-zM2t1mkodh0Mo_8h4Rx{6yY1OZ`=j2M10oGh5SO2pPEL)APT_K|TPeg=wi0C6NwQKPw+;f!=4&5# zF_5YA(LTfLfU?;RDqJ~mqyNIx*Sq&Q|dQv^$oIsv&XKT@0)PT zh35{zGICsINZY_s5~5$UW}~DA@t&2rbc(TD*q{~wu^Gmq#fz@F@s^{zpZee- z406~g(7`ic+4CPe-Fy3}%P&3`pDL@WSwe3%l)4P%;He0n-dZ%r5D*<+GyI8~xTd~t z%XX`u^N$1Y{a+v&F@RlEQ*-$XPgP-?AXD4?Se{fzQ%`0vL=|HlZ{0qffcv; z)4!Z~`Z*W%9yIKI`f^JJZe!7xDZg=u$=QT-J4gYg1TEsL#(o~>4ms_C3Ac7V<_L@? znRRNKNUKu(nN_tELl4-dbt|kVz&nPn#~xKyilrnxM+j=!e=Jd{nj)P=q|i{dT$!05A!y=BsJdvU$(0$3Aq|m{U*c(X66rbu|WO2toD2 z5ipb*RxInwYIcPKrSQ#wZd+`7=*C+t2|jv`cN`_yF7)+pzP|6HcT$VYDue*G*igj6 zyg*7DE)!Ls^FkC!&-3Kc%R5-85NoRs#3l|rj*y*!u;iuHJ0T2kER!iqdPIX}PbYF2tpu8D-A(cq>vj_tYan+J$s5cp@A!fU>Q=<^ez^cpRk3ORBDLvL%drb^(RMP5br4CCW zNoi`R&JeYYy-34|(6yIeh~-+>j2MOQy@diobgXJQ`^@|-(Q!N+@b+l}0$@%{{ zVA`B-e_phZmKCbX0-+TnUw>)84K{1vrmX_fCuI)TIsqpz0Td%aVb@8`XwINza!xSJ zIy=AwnXb&*3MN8CT@?|9=?aWaN&yKGH`}Bsm|K1rL&fc|ts0@QXMy6fN-R*l9IF;B z3V$$lsz6Iis}WOX!9t|%T-0r>`-cg3SpcPWsd^o1%c4C4>gpbyJmtpG zuJ&Z>)LG-B5Vr@W%;9`7$y<^_ri0IKJpOwqo$@BwH-Chaue~IC}dy_RH8#b zUXUraeyN;_c1M`vd1=yYYA4^TJE7_CC4maYr56rcE0ZF4yWLEUE1zswtv4Sx@|wpcPr)`A z0H;7$zcXjcngiWx)kTvow{2ITmGgE5&>OwPYD_ImueF9KxM1y-RvxzZ zfnm-9o(gBrpEvrgsTb{aFkVYIRl7iYkZ?*MMMBC2fW>~)`n9~*aUihO_R_H2xaO}! zl~;tDe+VjB4G~IZ;||}m1f!{emNSQPIR{rB(e;zX_-FDt#+{8(D-t!uuE*Z+#SkYHJG#xfwlAm zg2qdmPUt_rl)~Zak1}xtEWQw76TRV4QD#*?8EdHlF&YrAppzQfcApxO#^gyqUev?5Kp5%4B)E^^j*|hXSZOFo)h~CgMPb z5d$>UscE&CSBxT)ad|^uKX3kmi?6%^FMBb?6EA+T&eLizHl9Y2Gv~x-Tf|BsG7Y!e zmtT8R_uhk^eg0)okiG69fWkox_|NkmS!oUp!g%w4O z!nL-neCY1`wNhI^k~cusq@>Bh>Z;q`ol19Q?hyrvq>wN^Lb_10WJOq<1gfP9LD>@6 zq*^R{GQRiMaEd*ohr!llNoOruba#>HrY*b~0DF*s1eG;S5)jQZb;8aV)b+H@ zG4tmyNb`w<3;ppNyv635+%n>d>#w|I{dL!(*{)Or3M;R&GL3Gz^(0MG%P3n;p|D;1 zHIMGJ{StY6q%10zmSq*seD&D}KYdq$O>=V|F_QA2SXCnqEzRCM;*x0N(=x@|bRY9)4^xMisne7l#UZ zysSF<@Pi(>bIh>Q`?tZ4cuT5RS!qSwn{!2o(ik6fN?!5czZC9wHrNP=0v^KbI z{QIwCj3X;n2D*$`+&(kFWgrRc(G5_gWKe}j)X1SnlDN)$0q{^aED4&3xD*(VQAm=% z6xO(G52S|_J&e;OW#9@Kl%+a@1!UAgWWZNi5|8CQn-^slU%Vb8LrL8Eh0nUa zZ8h&BB>D22gqNx1DeFBra0u+hRM8n<&pLhB1=vyU-SNWF%*$=u!3jgfo31J8OszU-!#RZdNyUM#XiJ<9GP84gBx>M`%-7s_E4Ffb^vP$+ zn&1rqb_%k1te{_fd}>O8`WfjRJN6X%;KPqj>O17D^Dde5{g0xjkek}#A{n>k$8BGs&^iilTyHKf55=4|+@~ z?7(8;$&xf;pp<;b0uIN^!G|83d{p;7*WGk0rr%>1PbLc66pm}eXzc>K{U?Ns8SuCv zU`OG}w-^L36zV75``|I%dQZCV5e&bm8WJV95EErtbkSfUCuvzIv})O+&&gdMx@#;x z^@Xtw84)6j#paxtQSyWoQ2=vSs%xvqPMCCL*Hdo4^Ip8KCZ8l~Zezj(JnF5q;tFt#-f;rSw#+^}z-~&wcsX=ktET+|Ypfe>y}39aq>s!;(FSSwKFf zzhPRk9Z`kz_=p-%iXQ^;2nCTqt}iXf4fE&VM!nOpMil+6Gv!?T6lF|5c)*Zl4Vm`Qr|9oIb6))#W=(Z`9&y<# z3Ny1KJTOvOBqOEc^Kt+AX)q+G>3Ma`03YkkjItWL0xFeFpP5k&BI70^?IZ_R0%@ZA z>`V@0faRO-zCY)}|6;O#dawGFiaFU%*1xxEdB{BhMw(RoKQy=3)y& ze)kI=B;ZSvBaq8Le>D3TKK3+kI&k3X$NI1Uiq!gNmDq1QRu8dPzHh`IUK8*ZL(_XCBc^)kg1x{x{s)JE|p+5{9PV7wZ|f<$cJzx%E` zAAZmQyY1W=8!uxAc>-|8DKa4vs;abnJBb~?n^s`t{ia*Tbvd#BV^2Pd9uQ;VOlJQC z;}3Rfa@bW!V&sp61KAio!-g2AoY>{QJ8tRKy$eVO%#OCNajiDP^8yXltOtE#Ipn>K|1r$v|6*B`y(K|Pm67g<1@oK(1!lpLlXs6j6$NW z6AElmID%#>n6B@Vt6^;{z!3<`$FKb-I@z_}Xdrul~{s=aJS=*glaW4wXQQYK>AtiqB)Cj3Q7)_ftj;mg2&| zN=rW-&{`qmTr$hmPJ~*t@jC#0vRyo4t@dm*&c4P!#Y8p??m zqU#8I0b~Zp4G;i!ibn5$*rr?5*30KT*cMy^nv^~A$@{<8EWrXXhl{|dhVvi0Tl7#> z%?zngE3>?*v~{4Y2ryJ@2yk=?<8$dy$YSE4l!SJMA|nt5q#FUFXoq-k2#k`2PU9&q zb)r;iw&khInlZvC%hp99Do(Lae1}#+bNJ{hqvT1+a%CZgdlP^tlPK2&m3kOhg(gTf z>S`oK37Y_dUn-4&N+eMlvM`d+Osxr&^zbie&|oALf&?1MVZL(Xjnu#6aGTAY-Qr98Er$`_b-AaNj@HPlYevE>)tZ106rBZ{(swyjqfXYuD zoEvx-9pXMvB(qK<7fb6aH=K_ofWu|=Fn0F5_+Rdh~3S_=V9UVs+Ct- zxlhmT-A_0QgX5?q82l;0_t;BI)?0V&D=#_kp#Ao~;<}L^e)3s!6bjw&09%ywVn-82 z7gTq(-3%`mD1BziORvBA&Pm<6VCRMvS8R*5`-T&Ri_HMUA!M)XIV2#K#!Gl{m%I3zI*&*@PJdW1Psh@3ydXOszh0F+)Aa_~^`VdrhJlF}Zl|w<20*h3CCl#n)!&yls#0_Oe(xLkO!kd6{OX%QlV ziX2^1{9%I#4F6Ix*~wiV^=kS+7sr1Fn;%w4E=hI&SRK-ZnpW~bM0{vBAc7z>s~nsR zyo7^{xDan2CXl2|vJS{4+>lIHDhN_QGA#lKWLWl+!T57uW(Hmr8x5yYLzfHAR5EfP z$c0XoAn>6rD$ZdIOJ@ma%y@rr{IN&jPXB^`o`H=#F%-^kLdLT<@{v*>NZKedf8B#68+C@@9s8;>Zd_gVK03 z@F(*dC?lVM@T7*z9eyoW8*vCA89i5*qYruL?%VqHJ`s22ym&axQX|6+c-hzm#x>!d z4@y>Go9`w~CO`K=mlON_=gRAU`gyLZ9#k9(GC)R%{6PnV(=~(-oWw5e2h|VQ3bG%C zRepoa^;ceXw{ucHZkl1bOblNn0flQa)6fi|RsTr&2&f^93-}xY6#gTB z#qmixYJnms)^!G7CWn}45J*NA1yj@vz*MLdH-ojc_4WJiwfm&;qi?wKl8rW47uADq zHo4AIJI6oigiA{BK+vXji(zNAY9dhp-qYv134?K&&TuXdI zgW4Z3)UM{nC(yEhhmWf2s+(`U{m2t~Vft)s4feCE;FfC}u2>qXkjqcKL`lgpn{SO_ zAFlr_5#I?YYx3IHU;g;VLj73avN07{)jLUw8v^>JY$ ztzMw5P6<=GUI$PFE6Nd{f@dDe7wYUk_h1=EcJxGWL>M z^gs4+N+3*?z)Ak5aoXlBn$P+1r(qXd+N00u@4Pn+p8)gTBqbn)g=UU-USpjNR>q<* zH7JK>f(P^;7cQLg_2(H@1!vL7qA>+TcU*kY$*ia8Rjfb`W+7Fogo=xz`b}dZWY!A+ zfml`>Or-geO@1Vhk2=Cmtu|z9geEyb$|;~BG&JBK0U;5REXR}UNpfJ_2sKob5`;`r zhCuiV02KN2@P;MO4YE06xeUc$9^kRpB^~I6=h^s-;vxI*Szbot9KAU?m<|pJR_U~cqi9{CQ9MsFkMd> zbj5WyEv~G>;CMh3uDwA{LqagTO|usJZn$Y}{ZgoEgkZzJ$zOg_Q;6H9A2My{6sbZ! z2t_TYe5^*lfX%c8PD1e;nuI`}3xE$M5bw*4PXizS@g*}K2=PTYBqf?2(SW7WO$viB z4sD8U`N0MeY6YSq%)?2NKu86=1-1I#fFvS6oDh%(p)id%j^-`Tv$~vZy&Vow6G;+K zQGK&QQRNIi5e)uvAN)L{b;->w3=wuD@Ll$js_HgvTA$v(_rv#$JN4vl_>3Za9&50Y zPF9F9#v~a+U%mZm!>_pbj$5zae(No0vt=GTf+X%PSR{3F5oJ6^88UJm##ixT;Jpt& z?lthNb1%AV&JREEj_vfhp@tmBguEbd5Wd&grgh6RPw)G{#9NO&@(@^vYo3MU=_HIv z7MTtvyMz@LTeZdH7L5O3WyTFRkHw|&t#{tXn;29TscP(I#FBBf^gQQ7Hr}E&eRf#! zpk%Q%(6sNroxNZl_L%Y^K_g#swluKHUp8{4lKN{nDL`J4BL|M%Tp59ZvI2O05yK1> z?FL2Bh8+z@(DK=P^ae*Kq@NvYh);Mk>76*=jy(Lp`zPFX-dTe>v|BZM3-OspX+Xe#EEkw` z`>6AV4aVIuy&TpDIaY=BS!cOtCzpLjs$o$qUe?}q-@`|BKlRpecjARZ(*{h9_nF{o zkrJaf#~A+FVEuK5Uw-lU(ZhG#Zfndg(u;?|>{ww%te}X9Bsh{;QCnMb2zM9wMG2zc z|LD`{C-xmYV$?WzDYSAp&o#gn;;0Zru<6jM9X4C7V;xP+g5IQhSxM>q%EeQ^nb9=7 zLA25|18yKeLd>bVfN*9?T*Min4T17XN-i$LlIH?onB0J|!A(w!6~z`XscRie$=+MJ zl;U(iP;!F90;xnRh8bRyB4ci@1ffm&4d5vh#8TKOn>sQ}lMEaI2{+Fm8W9ZO%*JRe z%ZXOd5)ul6$o_L|$E#Z0uI{?ij(3b6fj7MyuD=fTuNhTJBO|933N2f<9MrGZ{S$B7 zdyn1lt~hu(j624Vrs1eRpZ&li;LV!hBgU6paXm)BUwh+i+T0YA6#u$|-Uipz?Y8rd z6K=cl(u>Y**KQRoYQyu8t&!g(p-9S0sI$Qx0X&080lW~teE7|$54&jIyal0;0@<3x zgt?}3$$1DYQxL>tq`lYONT!W5F&ThGVK2^_f&GFkYnV+}Uk~iEPa=aFETw3KVlM#7 zZ#fiKDi*D&R}^ZnTZz3?sfn^TSuB}VUt(z}1GdRuA{AuN|mh%JXRHf z!f3hy+5C1`i?q)&JPRdUJ?PC99;Yy1y~)NK-gNC{w~rpa%TC+T#GMQuuO@t5CDP;z zEnQJI+hl`rqpll%#lP2FqazKJQ_+Zaw!T@zU}glYKf+3mrteMr=;Z!q{qv$Le)#d1 z)NlA@l~F7mW>4YPyJy#9F(PzQ_bv$I;XtmzTnbVY_?3Vbwm@#m)$r<30iN!mkBF`_IU7a>yJ{K8Rnq$*iJe#(SYFE5H*j{+yKnk>>n zbQXOi!wOqL!<0SXRWXL0W`M^%nZ(U_jX3h}^3tnWSk=))eo^ESL-z{r{ml(SHQ5sr}7 z8pdZPmkNMsZa!|bC*J!I<_L|v^$sjs!;4V3stAj;yat`25>`vJZ`bb9^M_%{^sYPY z0Ap!HNcD-@M%#T25XdlSxdf|yLi_7|I{ot=ea?90)wi);0V-vb5YQ18<7v(yOQ&qS zO1lkLSrzyDOiR=-@wdu9KArm`rfIRAiplVb(HbDrSy=4fOl-@5h%E|mjZs08FO=y5 zKouDWT(0{Ypi7XF(mEjNxd{P>E;m8osGw4lkOLu~s*)xsN~*}lug*(pSHRc&`mB{V zAR0`cBf3Nqh?0Cv)9Gu)5MYg_1;DuIfCKkP*YIn|6j6iD>y zNfd?ltF4MDMz`H~?N(cM!bZomyjdB5vjlZQ$gluCoFV?hs?J~M{dVb9H+1Pa;Dwi_ zV&5KU8zQx%lFKFI`61&3Hwi=G3BO{|V0HC%)it%-ZL{U=w~V;@(hECwXsgE#%Bsm*l)|a%L}ow$MA73p zi_)G$nkGdOb&aKZax`;T2qcDa-1!NdC>a#QQNU#0CK$&fUjC6}{Y$uo0_GcN!8!wH zkjsI^ioqOhN=2v91fL*LN@+54HjE}ECP2&fN}CWkv5-Tgm=UIeP&gWnlkD~4M8X9o z@a$zQpvRUb%Hcqx5Y6qwi5B!>W;bymDyTxJB?wO3FJoX)J7HkyyO=K}hAbtLa4Cq} zL%KucLl{Y}-WUKW^!$VuYn-YiY6unepEN@$eK87=J$K)AaQ{Sy?GI@u!ITFWLBI9J>5t7+Mz%(Ga0J*tI34`|i0b-jv>T z??a=<-v0C4dFY-fL(_5H(;R$84vUwQlcgiXj3T2u-!EqLIsM#2_up&qz&`7*w+?2e za{VAet(8hZh0^P%D3d6HL$xp15~NunHkNF|Rv%U}g31hJQH`Ppl(KGJj@*5h&LhX% ze*Z&{S5{Tg9ui8Nvdl(cPbd&O-5a)*<}m88<|Wy`cy93l@1j}s6z~fE1hf&#~^qu#I4s#1TnI> zA46*D3O+Ot{IN*z3A0AyD9V>UVnJcc?}mg^Wd}h!EgZ(nD=|2nq2myPkwjH(Ridh( z6hraRBQ|Y2qd7chc)hxM35v=-NO5VEoQm5h6sk=S3d3dOdKbi|K}Dg>G28=*ura8b zAx}EQmNrg098BYzxUWI7&mj&q2(mbYcNS8NRK=|Wejta_V>{B5G`5(mAc2*lTgNrP zx*}QxRuF|7x&Uyd(ue9hu6&`wmCS^K(D+cGy0&)X4L2CvzxSaB?Ta29cELIvnnazs zhR|FcsatG;AyLYpa6^tV6cSLw9idb!N;RQSwp^Q5{d@P=dyid)-*np(lb?f1QnC=P zJ1C2XRI(bG(!~%2UO=d#oLcMyk4>KX`a6A2I-y&aqguCV%>(h&BO4wB&=EIjlGJ2S zDA-^psJAvV!aTVdO(q0usKkMoC%Sm?;#F2z@jvGeJM6%HM~ogf^|iNfcZ@Cmi>X%z zJvIQbsJQ5G=?5LyqRPcX&%Jo$bywg`4=Q||wkQG=ZnRd|a?ti`Yw7@9QQGA9x|+*h ze(wHH-mkzMZ!Q55jEg{dNhu!gUi;?jx4xZqNT+Si-}9h$t(T+L#0SP3>;E5l?*X7i zk-QJ@vY8}NMM1!XdMciJ=A3iH2ucPe7%-d}bH)HDAYuaH6tk!28P0g3pqS+p6G2Im z;{vFROmF*)6>Iy$=S#z3?kGJ6b%)5%mJuKf9Q~c z0L>rm&Iom~IYPCa9!JFB- zsSrqG$=bDl*|j6S*Ldg6*KE;xbD4Z&Cx;osaRBiVk{60vG?c?mA}6CiUA%PJMOO@- zHf=iHe1t60UVxH%r9x5csK~*h;k}yPsAX>VOA>+UVo#D0X51~z&dc9m8M4w zV)kT+{G$$^EpRmX+Lxu(qKB!`2IJE^9>qtk@x$kQXv4fZE-gG>A5G^ybKLlbBkYEo#8=MON-?C`t|KzR#IGv*9Dl<1#vV}5hpFGDD6`(OXGVDy!_(7UO!{ztcv;-$yc~h zl)6p9bOG+W@GuDA^O?EsVQU`9TG-$G2#~2c$YLw%&&5m9wZBTR>v7E>H1AHHh_r; zQ`=OX@mNC6@g%2rp)A+obRw~8C1&tLftr<CY~BMZ1lj8TZi3s z(>7bRTT_XTIML&|bk0PI8n%bT%88bYqeb&(*Is_nghxm0zwe$H1Mqypl)2?S=pUG% zLP3J7x};+&pqQQpKl{?Fr=5Eq_7TE^S*(h&aa^_y9>X|}_aT}$Z+i7*1ILdUe#9XM zU=jiIDyH6B_G)F^B!3NYX_(VW!u5Dr$>(2ByZQF}kX6HSWYF^Bl2@iroABkQ*!@k- zA*zc%cpM??I)#g%x}X{#)tNlw=iwi{!vNkNCY5?(Y3(Kb|3IQ^LU^XN_6E543NNvh z)n2BBAf>E+t@Pwp#aF~!Lzu;D1Las1h@!D{lv#HN#DpZFSHd(RW<~klB_b(A9m%Hz zx(}vjUs;Z*0xlP|E;l79h9xKi`t~^X=)*DJDv!%Ds*wbmEzhZkX;v9X8U%8=-Tu5Y zKDu(-4Oeg6q6Idji!nHGg@mZa1Zf0GbyKCOGoCXqUW~WK?me?x|9`zVsjRdN56o)Q z4;sz01jG&I7s}xSJDA1pxYPC{@4J2IUAK1FcI#EEWIH)Cot_0)(@5#cuTJB$PQsa{ z_{*Mu_4Oy7e3qV$szYCLMYM0ec7sYYe#-E313T-jADF^WjP$?$W3=}w8X5rV#zG^~1oHBoaD{$JdnDE9kRnfDrDrLr zL<#(PWm!&1<#T9V^YIj^K}CgnnKMu2frO__cw|;%m%5reYPwnB7~WlIy3t05A94UM z`pQ%9Fj(#5u1^#3Fa}_fx@+gt@aFItolb%SX^E;~2|`E;M<|Z}@@H7MXAuoB`Gaf! zFTeS=SO3cfU2((AS+kMi>yHBF4ISd8S-F#sI}$TsS6(u(X_JkxM##1}s{$Uw6|3&x zRTSIj-S^<=FTS2;ttsHXpB=mGElVhFQjwxV`8KXs#=wyUz#8_>&Nj9};LErups?$AmU)4x$Q2?@Kw$UQ(D ztd(vfSYgMF7P$(7(jt!L=a~Y3mX?*_g(;f6N)hcqYbCIX6rWL4U#)T|Y7~*OwoBX( zlcPnYn{3yPX@J6t#rN@mI6FOd zn#@iu&D9z(D7aEStmxB(6v;~)KS($gS4mrJu{l<9hRqCGRG5Y1DjO^9f0{8%))9N@ z@=MfB30!{pN=tg{dZR|zsIg3Sog2b6395&kW#T4~z^t+`hGAa3>sg(SJnSHPEKJ1< zcKNfbok_I8?XV-ufRvS&E?T_gx?At=)VcSY|9Tfh(caMEtbN2*$|_0)49#-6_U+mZ zx$XMLM-9bdA-pArxjVi%N4umyMXAqTjcg$?1o+Nc+4~=U^y1%N<4;C=sr-qMj7thk z;}lCrL4A(=(FsRLdJyFh9vR?sF>}+u7!&aM^)RW5Ff+Yj{9^<1rcLd&tOIiugj6SA zEzIZ{;N)x1MvWSoI-F4o$V3YN7A{}D(#OcUhoy*N=I)K1Vv&$9U5oTtr>k?=)KIpG((4O_!$&l_8UbPChWk^(>46;2TqY?%%2wkye)$c2o!O3~n1rM^DWaPNX|E-v- z4&En%B5;}fA?)Vouce1SweVO9QH3atQj+e0&R;(t2lkgoat+{x1r>GS;>BnoN_};T z$BWMiX%`SiFmVaC%T9l4)POXn0|bgjSO0)nD4btBLip^9uOEN%X=2Sh1^`n2z-0o2 z%Mtt}9XgvEHYQJ!Xrp=Am5lP*DXgiTVEN5WPLAQ;t7+3F(%nea$R z@YO_oYpiphN5?*e>uxw9A3tQ($nlA(MLq>l$+4v!9)vtU;gP;Q&nYi0#T=0qqdD!W z$RL`)ix^7G6%`l#_{%R7o_;R%6~z!IqIvb*@WcTksd%$iXdcA1X2jY zcPPc^^>?$oj4}vZjT<)v69Vc8{G(G6&uFE;{WfR$ij{a9m_V!PtzhE?(w_|?<=7US z6P&5Dv@OJII|SUwqRI4ax7li^9e1D&5^UvdRi(<=DI6C2pw%sR-G9rS_b*?*B0(i^ zi2kHL{<{ZKK%kyQ+k!;2qX3D;fBF0xS;1z(IOwmfPB^ne#1q=NMi&$T!U@chMYixk z9#d8kqcMy$Ogjt+Ub76YfRS3)hd8gItaQQezh8ah?PqoGhu3r=1o}cpvVf}wOo#b= zsJccLQMu&d5TX~vz%2xcbX?}!E}^I2)z#QH>4vK=#RnA*K44$G!Hd^8toD-0RM4yMNk=u_`6_IHb#bn6M9C5S=jJ#ujJH^lTfdZMCfOC_ zTlbKfM>i!&YS48JH-GY+V$lKiFu%S@AiRbk4hjUSd^I!SxrbXU9wY{lTB0jTppwEW z=AD>88GE3x^^d%5w}NRNEQYp;&PSV^G2NNW%z*Zn2c7ht*a|TAu|$4 z7S@e`GR$nni;9Z@VkHDYhNDeCXwnv7N)5upweUub8)M-epabPZP7n{Eq9G_hf5GqA z+zM0MRn#39kVA~61f~8fJl=0|&FVFv49F~8sD-v?+d|GX%4jcu=J=xz|7h~3%!+JQ zS~R!NDB18@>A2f2sF%ar{~vrfdGJ+(_S$1t6f~-(MM6QWH-#VyNsfk+7&7RMb6y%B z&`>CJC|KwYSL47O6eaP0xC%=`ad^EUK=B*y;w*S>$t}Ec^y@^-cEnoe)O=rpLyYx;iDd%HTze5 z2Ut`l^^Ma5OeYlfm6*&Vs2j89%zg2dHwK>H9kvlAJ`J>PEtGK}tO%&;mzVFtoS*gIJqTP}RJmN%Qgs=%34r%dtk9H+KYtr55v|+fA+^s2fs4L{zwJ zNTou&U6Be>hMgX?wqYeW9S8tVXfmknb|0d_NCEAaTrkUKvONJ`x2rrXs)?q6wGNWO ze?~=SflDKQ5XrnG8l3_C39p1XPxYr>SD>uv=^}Gm8dyQWBsJx~(t!kmwRy89^a760 zOrl6TpxV-<%cg(-BQ946BcvnQqD|lcqDYgv1?h4)ZefX*`s@&+hR#@5x%TM84r<@F z4W=U^QVlf+LMo|D%vlxIuPFcW+iBw}`hA3fp zNg)%-5bzL_J$USoO8(qS|2VBn-(e#j!6$ab1agdfNb^BjM40%6e&ta9ciB0niv$oJ zeFzv$4g(Bs63#ul)5Ni(jymEH>5>A@sC-u;FR-{2n-M+p;@_7nrAc{$!R2MRc{;WT z@cvtFg8`m6z(agS0}e|%lbQ^QVLQUA>RmVAqFH%`M2TNlzjzKNhPB zic(}wfS{qsBUCatftpO4}v7Ot#3Ml~*bktuA1?#=*kG=Cq_Ta($82KJ2DajZc4I|H@2?0M_+|8_R?N zB6L8lHfedznWtb;ib5n8#T;&ySSD=sM6p{hW}*pjpXO zNx8jnng&*NBMPGGlLg>Zqnf$GM*G9Us4%*9*fL=vT1C{9uVsK5ehBFCu5r7Bahqha^9zE>N%P%}1kl2BrtAkyV#x@gB zvQ1nEaE6`q&Bz~q`sw`-Y0ArY5Se`-j@!_^+qB!M)#mHakkPYQy{9yGo8AsXTxoH= zlegamQ)YI0&BDGa5rbrVh)5tk4+T8s9tMn&Yb9svuq*~0REp9mo7eK!O^e#VXB1e& z2KBKt-7^5Biq=Jj`ugs_C*d)Gr&Llw@+x*4t*`(e_c;5slXu;DN4nWhtvoRVNQT;F zxWZGoQQ${9}3U| zfVxsL;u2*i(j|)n($sL#jhWl8zx_{-^9EgT*|opS$h<}XEx&4N# zaSe`roeU(8TP9`#0f9-Z-MeZnNEfZMLM5Bd^12L>>sNV5phZ7Bdg=APw4W20p09e<)qn z@{d&&3Tg#1=$f4bbr*A~TeR6cb^s8uNr+VYr~ml#=U;q{DU>8al?TMMo`Y&{`T^y! zglX_qm*8DC`7nJ7PrNOVhka32tXy&1km23VyZEcGr_m=2Q}twd@?1ATv={^7e1uJd zl&~Oaya|Z^16llZG*J&Rlvk!>#Vya&A;~pAzC;3O?YJO?_fW%j$`hY|`P8#}kA8F< z_U}ZeNBy4X2+Q!{dDtXLG&a9+bf}7Gp=WeDVeplMa9d5$n7&^lgtQ1n>EYAlPp1C# z^DmZBVHypkdLUyNzpxXBg!9$AEGe*^c&1t8N~ zWHr)&4A4eoL(Mz@0M`7ux@m!K>K`9NHOZt3YNcB-3k$H?Zo6%_jvWB9*-}}#Zo)Iq zXQNG|;=V9j@evMsmOXac<*G{u;`=V9^b-XHmBc1vr(l0@Ji;oTH086-y)PI(>hbke z>+nR*Ci92={f^K|*J1dQNEK0b;|=P6BS4RxsMm`*#0r|cu5Q|-@p(PZZPsKX{>Fo5 z!C41mAQF?=uecTXeet5}Zy9oSxAWh5e-aq1?vmM!hP9EEsEElVQMp`~Gf%-@Px#V+ zNskYRPQ(_a!Q!Tcix$hmr*N)CZUu=7Q$JT7a`Y)XbX-Ma0DD6!AQeZ5mXb>A72STs zN!v7QffZd8&-(~r=FMNn3`!lhi;I_$c>st&)S=yZ_}!Qn)`z>5EI>wY6QIfTsvWkY z$443!dGeIrg+3+RtyuUZO#~*1B_bZ$X!)hZ?FNa2DBaY$i)8^eJ!YC% zeh93I0%Q&aQSNne1?TlR=b0xTKH=yiuy_=Q>5Ft#gH-xUsd#`je_AqTgfM+KsgX92&sfA3?CJQz16R*p9)U^b)(lV4i? z^zH|fXkE4877yuY)E1cBprmZ*@n>AI|Ka6DC9Bu1lXY!`Xt0T0Ah9yAuvtaJ$V8JC zu))tt4W*B2B6&-7{2=ux8%7NW07qUnNwud^LcOxZjTvNsI{=UfZ&0)C*6neDmQ)V{ zwvZTAglKguR;_tp)FWu#3EgFg*+gUE4Mj9y@vK|B_M(2h{(8wkGyp8hju`_i1}kU- zt7LpBvyeVXIC<*l=k&f{*oZOd8wZga^nZ;gJ-&{~88qTCH~D`i7ULL?e%5=io?JT# z9T?WRZrOH=VMA`Y?~WTc-)s|pjn=f(kj!}hga|E9(%fUN`q>x$erlKA4~%*gZ;geP zsER&e09(sC3Zn)VmlO}a@{-oAH>aU4)8VfbxF-90+IRC7{O+yKakfAKEb1S0;Nef7 z)8~|(cH5|;Vr|v>RqNNT#3wX^uYD0W`djS(^YfBLsO_25Oecy{DxK3%#23FI@dQGG zLs1U{CuK4lSfRf-xP91~qzzv%1$yPzt2oNgOI6jIZnANQZMVY3nc!+Q z973IFou%H&xLruImT$iE!T6`1tHA&u)+x)#zBJL67Q~CM-L7AE!Fk>Bk?-cso8bGJ zZazFkh>mrM5{NQajavb9{deB?VCNqFKAtkwTw2=TfU!Dys=ZQ!twbhgU(r>V=U~-5 z>G>xG6(fIM!yzEzFq0Sws8yhTivnu08&HK*KvmW2Pd)LNiBF8~*5!0e4&e<7Dii96 zh)R9~&S=Ohi;GGsOhsAQvgONfxoc?GJ{SJ*<4?5bQWPZL6t{4)074mjR&3CKKDc-% z`#cBP8%YkQ@EbM@|MUHipoDj*SlC~#MiCfp4$xueO*enw#4}&)KH!0qyIg+I(f#*6 zc=y)r@T8Y-C{*>t9rG_s7vU}{71JXINo1W?O7!#7c~do`LQrrX$9TCTiN*v{D%+81 z07!-k7n+}%RLZWFAA?gJ5GRsNkq|U7(SJVzy!Sg&5ukYNvf^X+$u zV*n&(z|xo*&!~Qwc*Xv`ZUrA%EiIYy*%w`U z54`cVd$CVhco^`3gbTxp`^ck8)-XkZ(+U40q9O?y#KiL8u1krqtWzm*O!OhlHLW(; z_|BWI9X)&qzB;*jwfdg6gONe42nj?|$>J$aSy{>ZlRiGH`+#@f|EMDJ5$p(KehC@% zM9n$n_@j5*wIjCnGTti=mK$&cz2z&1b@m6*W7ImSPBI9aIQCH~YHq$~pfX_f>E7bYI) zFSsCxC@&q@S<9>c6cgKQi{vf z5o4Y>?VLVugtw@~#Z*ciuy8$#t;NZTT(nBFm%ERN%Cia zvIh(l0w(Nb$Nfi_(@(*R0g>EnmNFG4vc8%2A1u%aE%7kYlMNv3@fy_m?E0SCnVb*&!=*NmK%^Jn-b7v3uRKU81LVw!4=BWQFa*7+Q3Le zG-alb-&dw}<#g9&V5nqlG(5meylO7MQcI&MqloZ8CHOSe9A%5?Eds>CU3T7at1a72 z|KZ1yl0cBj&l=!wbxr;J%ZsnLe(0Sy;%m3GS5N3bXHc(R^jWdEM!PqMYahp5A&>&) zkicnHC*Pn!{Q>89KjN?h?;Sqs)z|-p325>^9j@h8X++>D!6-wV1qJ2hrQc2e5g#o% z{nX(5`l*u=fOgc4Olbme1W_s8#TK5 z+Di{Rc)#239{R;s-{ONaJaHdIRXEX894QRI*9EZ4(?yqG_t5Y=_u2C=(3#8RbR~iEIAgJ@}w~+PB?e=Il9Gv6`iZD8C2pFT_`3e)?qw48hAf zvUR0LOHN9zAyB)dUOfx|rNt!(LxKpYS6DQ6<#K%F6LSDSNOcHANgsj$AxKprr|AFj z4gnF-sf}LXRY~AqyUJ0aCRbEo6ck~nB)7#tAAyuWvO`36F>14=MK}lutO7)n2xnMv z(5P3ywNI@~QV0a-?CkE_CXg`vp4Ku8u&;&pxt2i#0@ZR8cLUG_HBz=uv1o8%p~|6Q z!v;qkdf>OyzN^&$0HO3^LFqr=e0#tpSKo8{^-VWwjMi@VDR4+hJ-pbTG|Z4gDl55G zSjv*7`agBYgz3JD8)j(-dU`3M*KMfG^| z71+DJ5_?k7#}`p)58QXpo&WUUs4-(78TZtxl`HY7AND*;gFY7{X2*P%VYc&yq8ahX>R6-zl8xb9+W_K=H@Phq8`|bVk*rzIrWRosU6@uWX z6@o2{002M$Nkl$>f$pZkje)`Cr!*3#FdA)j@HQs2Wvhu~1 zYh)jMq=9_dy0vR^RSk;EQ8lrs8inG~j$o;)Kq;C5z(dB>+w_M*pmflRp%l=Mq4`Hj zBfyi|9>jeiAY|$fS#cSxxJV@~)}*}%rgiqo&z_bUMR_Vaa-Ni?cR+2>e^fEdanf-| z;`>MRbffiHE-6G+A0W8B2*}H;t*F5%^J~_YCct0WdnA*D=+)?x8E+hVlZxW zX3hQ$y_?P?5=Cako!2+qH7)>>W!2ZH*h~ z4me3k^cT3i#hV=C9v-&GZhywZ8?=9R6L+RmQ2H+k#-CSOQu_0ZS=Zlo4?l4B(;(HO zpwy9A4ksgE4?;wM&hEhd_es9i5g^6qvLg$RXP2*BIdj&pLYDZO(4!I`lxa{>+PJ*J z41_31T)V8OtX#Nq1rad1E@h6G0Mg^hf%gJYMT17H5fo4CO&#$7NM zdp?!|M*ej1C5Vj4!c#}g#`f>s^_j7wJDqeKq|5vRso|)Ci2P|FOL&MB+Tik|xL)Bi zFZ})N9{s-m;YVuZpy$IuMT(^BTt$@cDPTv){>vAr_Vi>Q{-c`?l!`|sUDN0Qg{L)p z?%Hwe!$bc%s6XEBroBB#O4yh2a{M_My|CXO?nj@0`89I^6eTqd4YJa74k&dA+bI&p zs*3G9Y=eDP>>Iu)nMw)Us^Yk<$JItGSA2$zhcFDPC@E=1@66Mzm)M8&)oUvkuU;Jw zqjU0ch~}}*#E$>9M6m#~2DL0)%RIglfVpuNTfze=q(+Wz1!m8g zH-Etbe9$$>BP2U&<#>acX!V7-v0nw`aBN11E+$$_b{92aIClvxwV{CMAl{)J;Z0J&2kkp< z%<#ciUb=D1jn`DBd}1LefCEIMhCB8u&N0BN3?!z;HIQD3|A5c>97+|BOvV6;!iAPeu zuc9fS|6>O%CZmm=A@g_u(aS&sBER=SynjG6(1YyY1{5f{q2!~##%h`F=bT9!JCj`f zS1W;Z#g9h<^XAXL^7=cvpLYp8kHvbGnl$HH=>cZA5S@NmX>s4~XFoG;ROi!AM(-`N z4f#WhcN{(>c@MsNmpdQ$c*7T@SBdzg~ z;@F2yJoZRl+)xE_do8I0l_7%9C1Jh}gVqMFcwt}S$@k`)Z5nSYs~%=@?193ImMp`~ zM6%}8p8Nx?^1bNLsI-g<&ys-pxMa;LD`7(hZNVE-4zM9AUYhc_9suf2m5@j(l#D%H zh!ZMtiK^10NR*=P^wvn|4G%kbf4mcrZ8s}sgI?xO)YfRhnQjHJ^s^Xm=$zea;B9vg zTe5^dc9GFJ8YnLpP+#loZ83L^&Z~8+P42wuYCL$vV-MUK&{w@sR9srBc4(oYs7{u1 zVx~(#d>bxB48U)GmeAAzFRYAcrg6A8Qs}rx(#a8eI{`#YN9z&csJ2^d z{=mJr4ZZV*)~#DP8_C%`*}{0Kd)oKYzy9{SY~+TjHf00eLM*A+q-6`Hq@esERVZMC}rTXj{4+ zw#gF0Xmz;w>fgJ2d09F4RA{*II)vmU;OZ6APXo{v=c%xhkpZ32eUSj=(v&rttNUv z5DjAVq0^{@5FlOsdzZ%7m+D)~^sTIGnO`YlmI{8Ail9VSi`m83BsYNjn%a$WJLfB1ST-Z<-b@s;Oac;)mTexgnyR3%;5 zD5RTT^Oy{HXPtJ^3lkpg)vYrgfaBIjiL=yB^PWZO)5X>w65U&HA4C3Wwut<>^X7mN z=uhLTRhq+;x<0KXP3Mria=F%@od35fEg4>;pPz|0H_!u3__`}gUF7peGV zpLp0B=1>QS`wToheespoI(6yu5I*R!nm_0f0rZtlgeWF~jTV5QKx|s+3#fz1^jdV=ts3BsIc-q)3a6{n&c$ZB2|%u-~57&!}!T` zQ1=(;?L;#1LmY|1l`WPF^ROLA4ni7A@S~eXD7mBv{kIRwij0sbAk>zD=k?fU&)u9A z{b6n!DICUngdcwT8N0hH z1+uWu!Qd+f^&W5;?qRtGi%Qxl7OW>IRAD0mLTTVI6U!*EIPdyf?|Jq0x3PQT0sHO= zCiYd1?^EL;$ggw+ppg)s8NwRfaYnK^!7dk2V@JG4N8a<|E3Xe9Ip(_`e=aM*<^<58 zkUp10k09N*UXha|-;pS^&}ZeUHCTZPSzuM>(QZ-daCoc;ve_0ZNrDQ3A}A>+8li9! z_?dA&FZffRlV$R0QSm90g&)vZU&uI+NdkiFJ@LLBLUH!f4pFLX=Jn_@{|Y z76Q0GJNfvd-~adI0`ut#q)HKJu{OOM&J`Bn6Dx}rFJ9KTaU;0k1s^?efu+ca zyIC=4pv2)-0Er-j#R>2J2|KA`#H%6W6(vs3Ic4dP?oqEBpkwz7L6S0;juUvYV5c79%CLK zKYrr#7@zRm!cf#IfGu%~i|}IV(xuB=wrp;fCTd9XC0>>q0tCUNIv;Te(ojQaV*_b; zJ_2;pwi?`af*&A&6+8A|kW{0?#N`2VkGXxco z$Z3F4u7-Z`9hM92#AqXEczeCqCqjvG&(i#{!wz<#a=DSYWC`L@I$`T7XMl=G$3y`+NQLZ&E504KDX?zwxSF zcK#E+8WwFN5E&;mE?tA`vnqUX1omwBebJ)pZyj<@uL~x9JQWfXTA&CgIeuCGR4q5< zj4K^XaqRr3?M4r~Yxq64Zq>f+>Q$?811@D13ylnuX98LjCGgc8ZmMR?oXw_1h(c9a z^JY3b70HQYV%J2_DNu_dK}?g-Q=*kCSJCtaB@vPSqF5I%p)We5^Vb~(3{IGXD2@~B84OntSCd|kCP~*PBT$cHrcqv z&>=T(*{&__Kp8ty)glS;)ih2yQ)}pT0Sp6EKBaFlUUTDJ*k%DWpDKU!phUjT%3od- z%$tInVu(KBm?NHj^5F~n_o-iAAu}W>G8POCNHNyK0x)sPbPgo&g7ojt?%wasf4xWi zj%kL|;mR7Z#ZfJ*b{d#7N$798ajZA8)U>*2S zLC~otXaNsGI)ZZ+i}D`;YF`}$ffcnBt|czPF=Q=qJnX1o+iclxo|?^Tha5&hCvj_)%%yj#5L^g)EKbKf`<+*h%!tO9uSy@sWoe zd;mX@3#Pw$3?Tb$A4IThMRVBk1|4Y6{+C>Q+2ENoXGe%@m|$cCH6fqQA&@GTD1!_t z@~Oh|Q}KdF#y-7t`7+gSDEa`vgp?-mVBJ;EEKo8qlNz@A9Its_XROLIz*RS_N1hm} zc~b!hr<5%H(vcAzWOxWb@7M_&ehD#Gzl0xS)OZvyXf+LJqm)85g;9J!%jl3lZ~&kt zz*Uz#&Th}!O68>}A7clLk)i}a3KhNm7vxZK%z^g1;|?7jeqc!3)|*$-UZLV%{JMUQ zB*fsQBufhi7``hobJpw&|2nv5zd>JrGYt)i+foYqhRCKtAeZa7(+-c1y8pp@Zrgs_ zt??9#uchc3p4>p5pcBU}6)0VfAcR7|BRkkK;kg%1IjiTm3D01!7(TqugcRfIVlFAd z6S!=XCR#_uPe@u14y$xcmiaDxUN!&s-!C70Yp;QqfA!5Y`Xp8ytEEQv zpcHj>`glq#GQs3ry??#;;hEk0-#zRhUfW`ulfa6{>Qu#Qb0}`As_8%pY9T8jw>BCk z@lu)6U#j%5aoSdCqe&vN<)PA*hz05O>#_2y68cX<4v`*+@P2VTXcFMhKZx}|ku&}k`% zlvkAD8SLG|MxAj^-^eF<2?=dWnwP_wD5GYXZ~~4dSR-`VK4!It?7IT|| zcy%a^C?-0oEGxpxm@%haR#NiL`~U9TbKrf$N8`z}{(7ZG`fs0Q1?sf#e(ZnQ;NAl- z$2T6JA&tRsgwicATU%=@4bMouGl`ndB=`ctV>k^D6+npImY=ychCxD@K{SCdAuHgQ zHcRz0?(o4N%}RD-aX1;$@x?F3C2aUnsv~xYBlRMFO~n(n_&0y%RP@>rWKrmk8vw}P z`bek=n#6auoXth$xSg6~=Bf{L8!Ql(<})wf-aCQU)`dtSO}F*PD}p@&G0D5Mgo56b0s+HpI) zHFp19H@0otdQIgTq)@iQ>8c`G>epmZQQ)Zn)}G!ybkwu$l5Ws>qA0HmHX zK)$swMPH*-OrVzRF{pVb<0THPksor)b=O{gaYb1bNN)gJc45s{EOhJOXIa*j?b{6OO?rCkLL_ zv!c9$UoS$X3I-vBf=E0zl%U?WqHO9HUv)e0;+yZfe<@x-D}!lyXc+A!;1uH*6NuNs z#*oBX&42H;N59`~}7B z@I}f0*c4Gtsg$M0)KIz|hl2AMmZ_dm4Kar(Z=!!pAX?jNAUAygkiJA0 zj997~wfG?H&1_nPxKghUphk#A$svtIpBRFuKDG87Hsr!z?`~&5GV~^!;E;~Lt^4=F-d2QH;G1IY+kn9kkJ;6Q^ zbS2-03Nq*c9loDa_QOxV^dEHf@kbwi$pwA4Y~L1j47L!628?J*Zy2Q45UQ)B00ZG1 zI0Jsb+9W*L{_Lx-9~}L}TkrlGn*+#IzxXpyGAi3j5l5>8fv57q*C5~}iUW-|k_&h& z*OOO3fEmBiqHw#zAcR(+h9YoVYyp-pB!u|IA2h+cWZbA}_Zw7rFOn#prRAb%xF}_0 z35$nIlN5zQkR-o`Rb=a^gvh{Ue!8mTq}$UKzjjLDbZjtYCEYPO9h9Z z&A7gFV8B@{#qT7$5mXmkg1Ko<47y^Q4*WW)Rs>lnxnvO1)g48YS#(mt(&ChPVSpr? z1HW^%5yCMCB#BhXs*-53DcT2YjW%0D-Pa&pF8du{>`-SfDex`s#06{ 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 str: return f"openai/{model}" +class OpenAICompatibleLLMParameters(BaseChatCompletionParameters): + """See https://docs.litellm.ai/docs/providers/openai_compatible/.""" + + api_key: str | None = None + api_base: str + + @staticmethod + def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: + adapter_metadata["model"] = OpenAICompatibleLLMParameters.validate_model( + adapter_metadata + ) + api_key = adapter_metadata.get("api_key") + if isinstance(api_key, str) and not api_key.strip(): + adapter_metadata["api_key"] = None + return OpenAICompatibleLLMParameters(**adapter_metadata).model_dump() + + @staticmethod + def validate_model(adapter_metadata: dict[str, "Any"]) -> str: + model = str(adapter_metadata.get("model", "")).strip() + if not model: + raise ValueError("model is required for the OpenAI Compatible adapter.") + if model.startswith("custom_openai/"): + return model + return f"custom_openai/{model}" + + class AzureOpenAILLMParameters(BaseChatCompletionParameters): """See https://docs.litellm.ai/docs/providers/azure/#completion---using-azure_ad_token-api_base-api_version.""" diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py index c23a33390a..1da3590f51 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py @@ -8,6 +8,7 @@ from unstract.sdk1.adapters.llm1.bedrock import AWSBedrockLLMAdapter from unstract.sdk1.adapters.llm1.ollama import OllamaLLMAdapter from unstract.sdk1.adapters.llm1.openai import OpenAILLMAdapter +from unstract.sdk1.adapters.llm1.openai_compatible import OpenAICompatibleLLMAdapter from unstract.sdk1.adapters.llm1.vertexai import VertexAILLMAdapter adapters: dict[str, dict[str, Any]] = {} @@ -22,5 +23,6 @@ "AzureOpenAILLMAdapter", "OllamaLLMAdapter", "OpenAILLMAdapter", + "OpenAICompatibleLLMAdapter", "VertexAILLMAdapter", ] diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/openai_compatible.py b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/openai_compatible.py new file mode 100644 index 0000000000..3cb3ceafc4 --- /dev/null +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/openai_compatible.py @@ -0,0 +1,46 @@ +from typing import Any + +from unstract.sdk1.adapters.base1 import BaseAdapter, OpenAICompatibleLLMParameters +from unstract.sdk1.adapters.enums import AdapterTypes + +DESCRIPTION = ( + "Adapter for servers that implement the OpenAI Chat Completions API " + "(vLLM, LM Studio, self-hosted gateways, and third-party providers). " + "Use OpenAI for the official OpenAI service." +) + + +class OpenAICompatibleLLMAdapter(OpenAICompatibleLLMParameters, BaseAdapter): + @staticmethod + def get_id() -> str: + return "openaicompatible|b6d10f33-2c41-49fc-a8c2-58d2b247fc09" + + @staticmethod + def get_metadata() -> dict[str, Any]: + return { + "name": "OpenAI Compatible", + "version": "1.0.0", + "adapter": OpenAICompatibleLLMAdapter, + "description": DESCRIPTION, + "is_active": True, + } + + @staticmethod + def get_name() -> str: + return "OpenAI Compatible" + + @staticmethod + def get_description() -> str: + return DESCRIPTION + + @staticmethod + def get_provider() -> str: + return "custom_openai" + + @staticmethod + def get_icon() -> str: + return "/icons/adapter-icons/OpenAICompatible.png" + + @staticmethod + def get_adapter_type() -> AdapterTypes: + return AdapterTypes.LLM diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/custom_openai.json b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/custom_openai.json new file mode 100644 index 0000000000..8767ffdcf4 --- /dev/null +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/custom_openai.json @@ -0,0 +1,60 @@ +{ + "title": "OpenAI Compatible", + "type": "object", + "required": [ + "adapter_name", + "api_base" + ], + "properties": { + "adapter_name": { + "type": "string", + "title": "Name", + "default": "", + "description": "Provide a unique name for this adapter instance. Example: compatible-gateway-1" + }, + "api_key": { + "type": [ + "string", + "null" + ], + "title": "API Key", + "format": "password", + "description": "API key for your OpenAI-compatible endpoint. Leave empty if the endpoint does not require one." + }, + "model": { + "type": "string", + "title": "Model", + "description": "The model name expected by your OpenAI-compatible endpoint. Examples: gateway-model, gpt-4o-mini, openai/gpt-4o" + }, + "api_base": { + "type": "string", + "format": "url", + "title": "API Base", + "description": "Base URL for the OpenAI-compatible endpoint. Examples: https://gateway.example.com/v1, https://llm.example.net/openai/v1" + }, + "max_tokens": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Maximum Output Tokens", + "default": 4096, + "description": "Maximum number of output tokens to limit LLM replies. Leave it empty to use the provider default." + }, + "max_retries": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Max Retries", + "default": 5, + "description": "The maximum number of times to retry a request if it fails." + }, + "timeout": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Timeout", + "default": 900, + "description": "Timeout in seconds." + } + } +} diff --git a/unstract/sdk1/tests/test_openai_compatible_adapter.py b/unstract/sdk1/tests/test_openai_compatible_adapter.py new file mode 100644 index 0000000000..addbf080ce --- /dev/null +++ b/unstract/sdk1/tests/test_openai_compatible_adapter.py @@ -0,0 +1,172 @@ +import json +from functools import lru_cache +from importlib import import_module +from unittest.mock import MagicMock, patch + +from unstract.sdk1.adapters.base1 import OpenAICompatibleLLMParameters +from unstract.sdk1.adapters.constants import Common +from unstract.sdk1.adapters.llm1 import adapters +from unstract.sdk1.adapters.llm1.openai_compatible import OpenAICompatibleLLMAdapter + +OPENAI_COMPATIBLE_DESCRIPTION = ( + "Adapter for servers that implement the OpenAI Chat Completions API " + "(vLLM, LM Studio, self-hosted gateways, and third-party providers). " + "Use OpenAI for the official OpenAI service." +) + + +@lru_cache(maxsize=1) +def _load_llm_module() -> object: + import sys + from types import ModuleType + + # Stub python-magic so importing LLM does not depend on libmagic + # being available in the test environment. sys.modules entries set + # here must persist (no patch.dict) so litellm and other lazy-loaded + # modules stay resolvable across tests. + sys.modules.setdefault("magic", ModuleType("magic")) + return import_module("unstract.sdk1.llm") + + +def _load_llm_class() -> type: + return _load_llm_module().LLM + + +def test_openai_compatible_adapter_is_registered() -> None: + adapter_id = OpenAICompatibleLLMAdapter.get_id() + + assert adapter_id in adapters + assert adapters[adapter_id][Common.MODULE] is OpenAICompatibleLLMAdapter + + +def test_openai_compatible_validate_prefixes_model() -> None: + validated = OpenAICompatibleLLMParameters.validate( + { + "api_base": "https://gateway.example.com/v1", + "api_key": "test-key", + "model": "gateway-model", + } + ) + + assert validated["model"] == "custom_openai/gateway-model" + + +def test_openai_compatible_validate_preserves_prefixed_model() -> None: + validated = OpenAICompatibleLLMParameters.validate( + { + "api_base": "https://gateway.example.com/v1", + "model": "custom_openai/openai/gpt-4o", + } + ) + + assert validated["model"] == "custom_openai/openai/gpt-4o" + assert validated["api_key"] is None + + +def test_openai_compatible_validate_normalizes_blank_api_key_to_none() -> None: + validated = OpenAICompatibleLLMParameters.validate( + { + "api_base": "https://gateway.example.com/v1", + "api_key": " ", + "model": "gateway-model", + } + ) + + assert validated["api_key"] is None + + +def test_openai_compatible_schema_is_loadable() -> None: + schema = json.loads(OpenAICompatibleLLMAdapter.get_json_schema()) + + assert schema["title"] == "OpenAI Compatible" + assert schema["properties"]["api_key"]["type"] == ["string", "null"] + assert "default" not in schema["properties"]["model"] + assert "gateway-model" in schema["properties"]["model"]["description"] + assert "ERNIE" not in schema["properties"]["model"]["description"] + assert "qianfan" not in schema["properties"]["api_base"]["description"].lower() + assert "default" not in schema["properties"]["api_base"] + + +def test_openai_compatible_adapter_uses_distinct_description_and_icon() -> None: + metadata = OpenAICompatibleLLMAdapter.get_metadata() + + assert OpenAICompatibleLLMAdapter.get_description() == OPENAI_COMPATIBLE_DESCRIPTION + assert metadata["description"] == OPENAI_COMPATIBLE_DESCRIPTION + assert OpenAICompatibleLLMAdapter.get_icon() == ( + "/icons/adapter-icons/OpenAICompatible.png" + ) + + +def _build_llm_for_record_usage(llm_cls: type) -> object: + llm = llm_cls.__new__(llm_cls) + llm._platform_api_key = "platform-key" + llm.platform_kwargs = {"run_id": "run-1"} + llm._usage_kwargs = {} + llm._pending_usage = [] + llm.adapter = MagicMock() + llm.adapter.get_provider.return_value = "custom_openai" + return llm + + +def test_record_usage_uses_reported_prompt_tokens_without_estimating() -> None: + llm_module = _load_llm_module() + llm = _build_llm_for_record_usage(llm_module.LLM) + + with ( + patch.object(llm_module.litellm, "token_counter") as mock_token_counter, + patch.object(llm_module.litellm, "cost_per_token", return_value=(0.0, 0.0)), + ): + llm._record_usage( + model="custom_openai/gateway-model", + messages=[{"role": "user", "content": "hello"}], + usage={"prompt_tokens": 3, "completion_tokens": 4, "total_tokens": 7}, + llm_api="complete", + ) + + mock_token_counter.assert_not_called() + assert len(llm._pending_usage) == 1 + assert llm._pending_usage[0]["prompt_tokens"] == 3 + + +def test_record_usage_tolerates_unmapped_models_without_prompt_tokens() -> None: + llm_module = _load_llm_module() + llm = _build_llm_for_record_usage(llm_module.LLM) + + with ( + patch.object( + llm_module.litellm, "token_counter", side_effect=Exception("unmapped") + ), + patch.object(llm_module.litellm, "cost_per_token", return_value=(0.0, 0.0)), + patch.object(llm_module.logger, "warning") as mock_warning, + ): + llm._record_usage( + model="custom_openai/gateway-model", + messages=[{"role": "user", "content": "hello"}], + usage={"completion_tokens": 4, "total_tokens": 7}, + llm_api="complete", + ) + + assert len(llm._pending_usage) == 1 + assert llm._pending_usage[0]["prompt_tokens"] == 0 + assert "litellm.token_counter() fallback failed" in mock_warning.call_args.args[0] + + +def test_record_usage_uses_estimated_prompt_tokens_when_usage_has_none() -> None: + llm_module = _load_llm_module() + llm = _build_llm_for_record_usage(llm_module.LLM) + + with ( + patch.object( + llm_module.litellm, "token_counter", return_value=9 + ) as mock_token_counter, + patch.object(llm_module.litellm, "cost_per_token", return_value=(0.0, 0.0)), + ): + llm._record_usage( + model="custom_openai/gateway-model", + messages=[{"role": "user", "content": "hello"}], + usage={"completion_tokens": 4, "total_tokens": 13}, + llm_api="complete", + ) + + mock_token_counter.assert_called_once() + assert llm._pending_usage[0]["prompt_tokens"] == 9 From c1a42aaf234625537d33ed4c3e9b7d488e66025d Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Thu, 21 May 2026 12:44:51 +0530 Subject: [PATCH 08/10] ReverseMerge: V0.163.4 hotfix (#1980) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [HOTFIX] Use importlib.util.find_spec for pluggable worker discovery (#1918) * [FIX] Use importlib.util.find_spec for pluggable worker discovery _verify_pluggable_worker_exists() previously checked for the literal file `pluggable_worker//worker.py` on disk, which breaks when the plugin has been compiled to a .so (Nuitka, Cython, or any C extension) — the module is perfectly importable but the pre-check rejects it because only the .py extension is considered. Replace the filesystem check with importlib.util.find_spec(), which is Python's standard way to ask "is this module resolvable by the import system?". It honors every registered finder — source .py, compiled .so, bytecode .pyc, namespace packages, zipimports — so the function now matches what its docstring claims: verifying the module can be loaded, not that a specific file extension is present. Behavior is preserved for existing deployments: - Images with no `pluggable_worker//` subpackage → find_spec raises ModuleNotFoundError (ImportError subclass) → returns False. - Images with source .py → find_spec resolves the .py → returns True. - Images with compiled .so → find_spec resolves the .so → returns True. Co-Authored-By: Claude Opus 4.7 (1M context) * [FIX] Handle ValueError from find_spec in pluggable worker verification Greptile-flagged edge case: importlib.util.find_spec() can raise ValueError (not just ImportError) when sys.modules has a partially initialised module entry with __spec__ = None from a prior failed import. Broaden the except to catch both. Co-Authored-By: Claude Opus 4.7 (1M context) * [FIX] Resolve api-deployment worker directory from enum import path worker.py:452 did worker_type.value.replace("-", "_") to derive the on-disk dir name. All WorkerType enum values already use underscores, so the replace was a no-op; for API_DEPLOYMENT whose dir is "api-deployment" (hyphen), it resolved to "api_deployment" and the os.path.exists() check failed. Boot then logged a spurious "❌ Worker directory not found: /app/api_deployment" at ERROR level. The task registration path (builder + celery autodiscover via to_import_path) is unaffected, so this was purely log noise — but noise at ERROR level that masks real failures in log scans. Fix: derive the directory from the authoritative to_import_path() which already handles the hyphen case (api_deployment -> api-deployment). Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) * [HOTFIX] Add IAM Role / Instance Profile auth mode to AWS Bedrock adapter (#1944) * [FEAT] Allow Bedrock to fall through to boto3's default credential chain Match the S3/MinIO connector pattern: when AWS access keys are left blank on the Bedrock LLM and embedding adapter forms, drop them from the kwargs dict so boto3's default credential chain handles authentication. This unlocks IAM role / instance profile / IRSA / AWS Profile scenarios on hosts that already have ambient AWS credentials (e.g. EKS workers with IRSA, EC2 with an instance profile). - llm1/static/bedrock.json: clarify access-key descriptions to mention IRSA and instance profile (already non-required at v0.163.2 base). - embedding1/static/bedrock.json: drop aws_access_key_id and aws_secret_access_key from top-level required; same description fix; expose aws_profile_name for parity with the LLM form. - base1.py: AWSBedrockLLMParameters and AWSBedrockEmbeddingParameters now strip empty access-key values from the validated kwargs before returning, so empty strings don't override boto3's default chain. AWSBedrockEmbeddingParameters fields gain explicit None defaults and an aws_profile_name field. Backward-compatible: existing adapters with access keys filled in continue to work unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) * [FEAT] Add Authentication Type selector to Bedrock adapter form Add an explicit `auth_type` selector with two options, making the auth choice clear to users: - "Access Keys" (default): existing flow, keys required - "IAM Role / Instance Profile (on-prem AWS only)": no fields; relies on boto3's default credential chain (IRSA on EKS, task role on ECS, instance profile on EC2). Description on the selector explicitly notes this option is only for AWS-hosted Unstract deployments. The form-only auth_type field is stripped before LiteLLM validation in both AWSBedrockLLMParameters.validate() and AWSBedrockEmbeddingParameters. validate(). Empty access keys continue to be stripped so boto3 falls through to the default chain even when the access_keys arm is selected without values (matches the S3/MinIO connector pattern). Backward-compatible: legacy adapters without auth_type behave as "Access Keys" mode (the default), and existing keys are forwarded unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) * [REVIEW] Address Bedrock auth_type review feedback Fixes the P0/P1 issues raised by greptile-apps and jaseemjaskp on PR #1944. Behaviour fixes: - Stale-key leak in IAM Role mode: switching an existing adapter from Access Keys to IAM Role would carry truthy stored access keys through the strip-empty-only loop, so boto3 silently authenticated with the old long-lived credentials instead of falling through to the host's IRSA / instance-profile identity. Both LLM and embedding paths were affected. - Silent acceptance of unknown auth_type: a typo (e.g. "access_key") or a malformed payload from a non-UI client passed through the dict comprehension untouched, with no enum guard. - Cross-field validation gap: explicit Access Keys mode with blank or whitespace-only values silently fell through to the default credential chain instead of surfacing the misconfiguration. Implementation: - Add a module-level _resolve_bedrock_aws_credentials helper used by both AWSBedrockLLMParameters.validate() and AWSBedrock EmbeddingParameters.validate(), so the auth-type contract is expressed once. - Validates auth_type against an allowlist (None | "access_keys" | "iam_role"); raises ValueError on anything else. - iam_role: unconditionally drops aws_access_key_id and aws_secret_access_key. - access_keys (explicit): requires non-blank values; raises ValueError if either is empty or whitespace-only. - Legacy (auth_type absent): retains the lenient strip behaviour so pre-PR adapter configurations continue to deserialise unchanged. - Restore aws_region_name as required (no `= None` default) on AWSBedrockEmbeddingParameters; only credentials may legitimately be absent. - Drop the orphan aws_profile_name field from embedding1/static/bedrock.json: it was added for parity with the LLM form but lives outside the auth_type oneOf and contradicts the selector's "no further input" semantics. The LLM form already had aws_profile_name pre-PR and is left alone for backwards compatibility. Tests: - New tests/test_bedrock_adapter.py covers 15 cases across LLM and embedding adapters: legacy-no-auth-type, explicit access_keys with valid/blank/whitespace keys, iam_role with stale/no keys, unknown auth_type rejection, cross-field validation, and preservation of unrelated params (model_id, aws_profile_name, region, thinking). Skipped (P2 nice-to-have): - Comment-scope clarification, MinIO reference rewording, validate-mutates-caller'\''s-dict, and the LLM form description nit about aws_profile_name visibility. These don'\''t change behaviour and can be addressed in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [HOTFIX] Bump litellm to 1.83.10 from PyPI to clear CVE-2026-42208 (#1976) Hotfix for cloud v0.159.3 (OSS v0.163.4). Customer scanner flagged litellm 1.82.3 for CVE-2026-42208 (SQL injection in litellm proxy auth path, affects 1.81.16-1.83.6). We do not use litellm.proxy, but vulnerability scanners flag the installed package regardless of which code path is reachable. Bump to 1.83.10 — the exact version recommended by the upstream advisory (v1.83.10-stable) and the smallest jump that clears the CVE range while keeping python-dotenv==1.0.1 compatible (1.83.14 would force bumping python-dotenv across 7+ pyproject.toml files). Only tiktoken needed to move 0.9 -> 0.12 to satisfy litellm's pin. Switch source back to PyPI now that the PyPI quarantine is over, reversing the temporary fork in #1873. Cohere embed timeout patch: verified that litellm/llms/cohere/embed/handler.py is byte-identical between v1.82.3, v1.83.10-stable, and v1.83.14-stable (the timeout-not-forwarded bug fixed in #1848 is still present upstream — BerriAI/litellm#14635 remains OPEN). Version guard bumped 1.82.3 -> 1.83.10; 6/6 patch tests pass on the new version, confirming the monkey-patch still binds correctly. Other cleanup from #1873: - Drop git apt-install from worker-unified and tool Dockerfiles (no git-sourced deps remain in any uv.lock) - Bump tool versions: structure 0.0.100 -> 0.0.101, classifier 0.0.79 -> 0.0.80, text_extractor 0.0.75 -> 0.0.76 Note on root uv.lock churn: the v0.163.4 root uv.lock had a pre-existing corruption (banks v2.4.1 entry pointing at banks-2.2.0 wheel) that blocked incremental resolution. Regenerated from scratch. Co-authored-by: Claude Opus 4.7 (1M context) * [FIX] Align cohere patch docstring with version-guard semantics Reviewer flagged that the docstring claimed the patch is "confirmed in every release between 1.82.3 and 1.83.14-stable", but the guard at _PATCHED_LITELLM_VERSION activates only on the exact pinned version. A future maintainer reading the old text could reasonably expect bumping to e.g. 1.83.11 to keep the fix active; in reality it silently turns off. Rewritten to reference _PATCHED_LITELLM_VERSION as the single source of truth and to drop the rot-prone "as of 2026-05-20" calendar date. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- backend/sample.env | 4 +- backend/uv.lock | 53 ++--- docker/dockerfiles/worker-unified.Dockerfile | 1 - platform-service/uv.lock | 198 +++++++++-------- unstract/filesystem/uv.lock | 178 ++++++++------- unstract/sdk1/pyproject.toml | 5 +- .../sdk1/patches/litellm_cohere_timeout.py | 12 +- unstract/sdk1/uv.lock | 202 +++++++++--------- .../tool_registry_config/public_tools.json | 12 +- unstract/tool-registry/uv.lock | 178 ++++++++------- uv.lock | 183 ++++++++-------- workers/uv.lock | 91 +++++--- 12 files changed, 601 insertions(+), 516 deletions(-) diff --git a/backend/sample.env b/backend/sample.env index 58461aade3..0bea8efbc8 100644 --- a/backend/sample.env +++ b/backend/sample.env @@ -95,9 +95,9 @@ PROMPT_STUDIO_FILE_PATH=/app/prompt-studio-data # Structure Tool Image (Runs prompt studio exported tools) # https://hub.docker.com/r/unstract/tool-structure -STRUCTURE_TOOL_IMAGE_URL="docker:unstract/tool-structure:0.0.100" +STRUCTURE_TOOL_IMAGE_URL="docker:unstract/tool-structure:0.0.101" STRUCTURE_TOOL_IMAGE_NAME="unstract/tool-structure" -STRUCTURE_TOOL_IMAGE_TAG="0.0.100" +STRUCTURE_TOOL_IMAGE_TAG="0.0.101" # Feature Flags EVALUATION_SERVER_IP=unstract-flipt diff --git a/backend/uv.lock b/backend/uv.lock index e2fc56b1b2..04d6894c1e 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -576,14 +576,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.1" +version = "8.1.8" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] @@ -1537,14 +1537,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.1" +version = "8.5.0" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -1652,7 +1652,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.26.0" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1660,9 +1660,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -1694,8 +1694,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -1710,6 +1710,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -3525,20 +3529,21 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] @@ -3599,7 +3604,7 @@ wheels = [ [[package]] name = "typer" -version = "0.24.1" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -3607,9 +3612,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" }, ] [[package]] @@ -3960,7 +3965,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -3976,7 +3981,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../unstract/core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/docker/dockerfiles/worker-unified.Dockerfile b/docker/dockerfiles/worker-unified.Dockerfile index 133a3be2f9..fec01aca7c 100644 --- a/docker/dockerfiles/worker-unified.Dockerfile +++ b/docker/dockerfiles/worker-unified.Dockerfile @@ -22,7 +22,6 @@ RUN apt-get update \ build-essential \ curl \ gcc \ - git \ libmagic-dev \ libssl-dev \ pkg-config \ diff --git a/platform-service/uv.lock b/platform-service/uv.lock index 9af6dfee65..a90ac4004a 100644 --- a/platform-service/uv.lock +++ b/platform-service/uv.lock @@ -50,7 +50,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.12.15" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -61,25 +61,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } +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/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" }, - { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" }, - { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" }, - { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" }, - { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" }, - { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" }, - { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" }, - { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" }, + { 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" }, ] [[package]] @@ -385,14 +385,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] @@ -533,21 +533,21 @@ wheels = [ [[package]] name = "fastuuid" -version = "0.13.5" +version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/80/3c16a1edad2e6cd82fbd15ac998cc1b881f478bf1f80ca717d941c441874/fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1", size = 18255, upload-time = "2025-09-26T09:05:38.281Z" } +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/21/36/434f137c5970cac19e57834e1f7680e85301619d49891618c00666700c61/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a", size = 494638, upload-time = "2025-09-26T09:14:58.695Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3c/083de2ac007b2b305523b9c006dba5051e5afd87a626ef1a39f76e2c6b82/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7", size = 253138, upload-time = "2025-09-26T09:13:33.283Z" }, - { url = "https://files.pythonhosted.org/packages/73/5e/630cffa1c8775db526e39e9e4c5c7db0c27be0786bb21ba82c912ae19f63/fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202", size = 244521, upload-time = "2025-09-26T09:14:40.682Z" }, - { url = "https://files.pythonhosted.org/packages/4d/51/55d78705f4fbdadf88fb40f382f508d6c7a4941ceddd7825fafebb4cc778/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e", size = 271557, upload-time = "2025-09-26T09:15:09.75Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2b/1b89e90a8635e5587ccdbbeb169c590672ce7637880f2c047482a0359950/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854", size = 272334, upload-time = "2025-09-26T09:07:48.865Z" }, - { url = "https://files.pythonhosted.org/packages/0c/06/4c8207894eeb30414999e5c3f66ac039bc4003437eb4060d8a1bceb4cc6f/fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86", size = 290594, upload-time = "2025-09-26T09:12:54.124Z" }, - { url = "https://files.pythonhosted.org/packages/50/69/96d221931a31d77a47cc2487bdfacfb3091edfc2e7a04b1795df1aec05df/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952", size = 452835, upload-time = "2025-09-26T09:14:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/25/ef/bf045f0a47dcec96247497ef3f7a31d86ebc074330e2dccc34b8dbc0468a/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9", size = 468225, upload-time = "2025-09-26T09:13:38.585Z" }, - { url = "https://files.pythonhosted.org/packages/30/46/4817ab5a3778927155a4bde92540d4c4fa996161ec8b8e080c8928b0984e/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3", size = 444907, upload-time = "2025-09-26T09:14:30.163Z" }, - { url = "https://files.pythonhosted.org/packages/80/27/ab284117ce4dc9b356a7196bdbf220510285f201d27f1f078592cdc8187b/fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85", size = 145415, upload-time = "2025-09-26T09:08:59.494Z" }, - { url = "https://files.pythonhosted.org/packages/f4/0c/f970a4222773b248931819f8940800b760283216ca3dda173ed027e94bdd/fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a", size = 150840, upload-time = "2025-09-26T09:13:46.115Z" }, + { 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" }, ] [[package]] @@ -978,14 +978,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.5.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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -1067,7 +1067,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1075,9 +1075,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -1109,8 +1109,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -1125,6 +1125,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -1590,7 +1594,7 @@ wheels = [ [[package]] name = "openai" -version = "2.16.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1602,9 +1606,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/55/13/17e87641b89b74552ed408a92b231283786523edddc95f3545809fab673c/openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", size = 658717, upload-time = "2026-02-24T20:02:07.958Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94", size = 1120122, upload-time = "2026-02-24T20:02:05.669Z" }, ] [[package]] @@ -2009,7 +2013,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.9" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2017,34 +2021,38 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } +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/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, + { 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.33.2" +version = "2.41.5" 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/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" }, +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/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]] @@ -2445,43 +2453,47 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] name = "tokenizers" -version = "0.15.2" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91", size = 320256, upload-time = "2024-02-12T02:28:50.62Z" } +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/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670", size = 2574444, upload-time = "2024-02-12T02:25:27.417Z" }, - { url = "https://files.pythonhosted.org/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51", size = 2411608, upload-time = "2024-02-12T02:25:29.74Z" }, - { url = "https://files.pythonhosted.org/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98", size = 3652367, upload-time = "2024-02-12T02:25:32.079Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66", size = 3529509, upload-time = "2024-02-12T02:25:34.042Z" }, - { url = "https://files.pythonhosted.org/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd", size = 3396516, upload-time = "2024-02-12T02:25:35.884Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38", size = 3918811, upload-time = "2024-02-12T02:25:37.85Z" }, - { url = "https://files.pythonhosted.org/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c", size = 4025494, upload-time = "2024-02-12T02:25:40.247Z" }, - { url = "https://files.pythonhosted.org/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456", size = 3575314, upload-time = "2024-02-12T02:25:42.212Z" }, - { url = "https://files.pythonhosted.org/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834", size = 9682779, upload-time = "2024-02-12T02:25:44.027Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d", size = 9995614, upload-time = "2024-02-12T02:25:46.804Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b", size = 2011030, upload-time = "2024-02-12T02:25:49.829Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221", size = 2180523, upload-time = "2024-02-12T02:25:51.542Z" }, + { 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]] @@ -2520,14 +2532,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" 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" } +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/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" }, + { 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]] @@ -2704,7 +2716,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -2720,7 +2732,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../unstract/core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/unstract/filesystem/uv.lock b/unstract/filesystem/uv.lock index 616a330266..518ebd00fa 100644 --- a/unstract/filesystem/uv.lock +++ b/unstract/filesystem/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.11.16" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -24,36 +24,38 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/d9/1c4721d143e14af753f2bf5e3b681883e1f24b592c0482df6fa6e33597fa/aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8", size = 7676826, upload-time = "2025-04-02T02:17:44.74Z" } +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/db/38/100d01cbc60553743baf0fba658cb125f8ad674a8a771f765cdc155a890d/aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27", size = 704881, upload-time = "2025-04-02T02:16:09.26Z" }, - { url = "https://files.pythonhosted.org/packages/21/ed/b4102bb6245e36591209e29f03fe87e7956e54cb604ee12e20f7eb47f994/aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713", size = 464564, upload-time = "2025-04-02T02:16:10.781Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e1/a9ab6c47b62ecee080eeb33acd5352b40ecad08fb2d0779bcc6739271745/aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb", size = 456548, upload-time = "2025-04-02T02:16:12.764Z" }, - { url = "https://files.pythonhosted.org/packages/80/ad/216c6f71bdff2becce6c8776f0aa32cb0fa5d83008d13b49c3208d2e4016/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321", size = 1691749, upload-time = "2025-04-02T02:16:14.304Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ea/7df7bcd3f4e734301605f686ffc87993f2d51b7acb6bcc9b980af223f297/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e", size = 1736874, upload-time = "2025-04-02T02:16:16.538Z" }, - { url = "https://files.pythonhosted.org/packages/51/41/c7724b9c87a29b7cfd1202ec6446bae8524a751473d25e2ff438bc9a02bf/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c", size = 1786885, upload-time = "2025-04-02T02:16:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/86/b3/f61f8492fa6569fa87927ad35a40c159408862f7e8e70deaaead349e2fba/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce", size = 1698059, upload-time = "2025-04-02T02:16:20.234Z" }, - { url = "https://files.pythonhosted.org/packages/ce/be/7097cf860a9ce8bbb0e8960704e12869e111abcd3fbd245153373079ccec/aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e", size = 1626527, upload-time = "2025-04-02T02:16:22.092Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1d/aaa841c340e8c143a8d53a1f644c2a2961c58cfa26e7b398d6bf75cf5d23/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b", size = 1644036, upload-time = "2025-04-02T02:16:23.707Z" }, - { url = "https://files.pythonhosted.org/packages/2c/88/59d870f76e9345e2b149f158074e78db457985c2b4da713038d9da3020a8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540", size = 1685270, upload-time = "2025-04-02T02:16:25.874Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b1/c6686948d4c79c3745595efc469a9f8a43cab3c7efc0b5991be65d9e8cb8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b", size = 1650852, upload-time = "2025-04-02T02:16:27.556Z" }, - { url = "https://files.pythonhosted.org/packages/fe/94/3e42a6916fd3441721941e0f1b8438e1ce2a4c49af0e28e0d3c950c9b3c9/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e", size = 1704481, upload-time = "2025-04-02T02:16:29.573Z" }, - { url = "https://files.pythonhosted.org/packages/b1/6d/6ab5854ff59b27075c7a8c610597d2b6c38945f9a1284ee8758bc3720ff6/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c", size = 1735370, upload-time = "2025-04-02T02:16:31.191Z" }, - { url = "https://files.pythonhosted.org/packages/73/2a/08a68eec3c99a6659067d271d7553e4d490a0828d588e1daa3970dc2b771/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71", size = 1697619, upload-time = "2025-04-02T02:16:32.873Z" }, - { url = "https://files.pythonhosted.org/packages/61/d5/fea8dbbfb0cd68fbb56f0ae913270a79422d9a41da442a624febf72d2aaf/aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2", size = 411710, upload-time = "2025-04-02T02:16:34.525Z" }, - { url = "https://files.pythonhosted.org/packages/33/fb/41cde15fbe51365024550bf77b95a4fc84ef41365705c946da0421f0e1e0/aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682", size = 438012, upload-time = "2025-04-02T02:16:36.103Z" }, + { 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" }, ] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload-time = "2024-12-13T17:10:40.86Z" } +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/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" }, + { 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]] @@ -561,14 +563,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.5.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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -619,7 +621,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -627,9 +629,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -661,8 +663,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -677,6 +679,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -1102,7 +1108,7 @@ wheels = [ [[package]] name = "openai" -version = "2.16.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1114,9 +1120,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/55/13/17e87641b89b74552ed408a92b231283786523edddc95f3545809fab673c/openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", size = 658717, upload-time = "2026-02-24T20:02:07.958Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94", size = 1120122, upload-time = "2026-02-24T20:02:05.669Z" }, ] [[package]] @@ -1323,7 +1329,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1331,34 +1337,38 @@ dependencies = [ { 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" } +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/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { 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.33.2" +version = "2.41.5" 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/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" }, +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/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]] @@ -1662,43 +1672,47 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] name = "tokenizers" -version = "0.15.2" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91", size = 320256, upload-time = "2024-02-12T02:28:50.62Z" } +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/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670", size = 2574444, upload-time = "2024-02-12T02:25:27.417Z" }, - { url = "https://files.pythonhosted.org/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51", size = 2411608, upload-time = "2024-02-12T02:25:29.74Z" }, - { url = "https://files.pythonhosted.org/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98", size = 3652367, upload-time = "2024-02-12T02:25:32.079Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66", size = 3529509, upload-time = "2024-02-12T02:25:34.042Z" }, - { url = "https://files.pythonhosted.org/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd", size = 3396516, upload-time = "2024-02-12T02:25:35.884Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38", size = 3918811, upload-time = "2024-02-12T02:25:37.85Z" }, - { url = "https://files.pythonhosted.org/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c", size = 4025494, upload-time = "2024-02-12T02:25:40.247Z" }, - { url = "https://files.pythonhosted.org/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456", size = 3575314, upload-time = "2024-02-12T02:25:42.212Z" }, - { url = "https://files.pythonhosted.org/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834", size = 9682779, upload-time = "2024-02-12T02:25:44.027Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d", size = 9995614, upload-time = "2024-02-12T02:25:46.804Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b", size = 2011030, upload-time = "2024-02-12T02:25:49.829Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221", size = 2180523, upload-time = "2024-02-12T02:25:51.542Z" }, + { 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]] @@ -1715,11 +1729,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.13.1" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633, upload-time = "2025-04-03T16:11:20.955Z" } +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/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739, upload-time = "2025-04-03T16:11:19.281Z" }, + { 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]] @@ -1737,14 +1751,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } +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/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, + { 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]] @@ -1840,7 +1854,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -1856,7 +1870,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/unstract/sdk1/pyproject.toml b/unstract/sdk1/pyproject.toml index a67c12c833..26fa9fb697 100644 --- a/unstract/sdk1/pyproject.toml +++ b/unstract/sdk1/pyproject.toml @@ -26,8 +26,8 @@ dependencies = [ "python-magic~=0.4.27", "python-dotenv==1.0.1", # # Adapter changes - "tiktoken~=0.9.0", - "litellm", + "tiktoken~=0.12.0", + "litellm==1.83.10", "llama-index>=0.14.13", "llama-index-vector-stores-postgres>=0.7.3", "llama-index-vector-stores-milvus>=0.9.6", @@ -79,7 +79,6 @@ test = [ [tool.uv.sources] unstract-core = { path = "../core", editable = true } -litellm = { git = "https://github.com/Zipstack/litellm.git", tag = "v1.82.3" } [build-system] requires = ["hatchling"] diff --git a/unstract/sdk1/src/unstract/sdk1/patches/litellm_cohere_timeout.py b/unstract/sdk1/src/unstract/sdk1/patches/litellm_cohere_timeout.py index 4090881c37..7a0a028369 100644 --- a/unstract/sdk1/src/unstract/sdk1/patches/litellm_cohere_timeout.py +++ b/unstract/sdk1/src/unstract/sdk1/patches/litellm_cohere_timeout.py @@ -4,8 +4,10 @@ receive a `timeout` parameter but don't forward it to client.post(), causing "Connection timed out after None seconds" errors. -Affected litellm version: 1.82.3 (also present on latest main as of -2026-03-10). +Affected litellm version: this patch activates only on the pinned +version in pyproject.toml (see _PATCHED_LITELLM_VERSION below). The +underlying bug has been observed upstream across 1.82.3 → 1.83.14; +any version mismatch logs and skips so the upgrade is reviewed. Activation: This patch is imported as a side-effect from unstract.sdk1.embedding. Any code path that invokes Bedrock Cohere @@ -27,7 +29,7 @@ # Only apply the patch on the exact litellm version it was written for. # Any other version (newer or older) skips the patch with a visible # warning so engineers know to verify compatibility. -_PATCHED_LITELLM_VERSION = "1.82.3" +_PATCHED_LITELLM_VERSION = "1.83.10" _litellm_version = importlib.metadata.version("litellm") _SKIP_PATCH = Version(_litellm_version) != Version(_PATCHED_LITELLM_VERSION) if _SKIP_PATCH: @@ -68,7 +70,7 @@ _DEFAULT_TIMEOUT = httpx.Timeout(None) - # Copied from litellm 1.82.3 cohere/embed/handler.py async_embedding(). + # Copied from litellm 1.83.10 cohere/embed/handler.py async_embedding(). # ONLY CHANGE: Added timeout=timeout to the client.post() call. # Source: litellm/llms/cohere/embed/handler.py::async_embedding async def _patched_async_embedding( # type: ignore[return] # noqa: ANN202 @@ -136,7 +138,7 @@ async def _patched_async_embedding( # type: ignore[return] # noqa: ANN202 input=input, ) - # Copied from litellm 1.82.3 cohere/embed/handler.py embedding(). + # Copied from litellm 1.83.10 cohere/embed/handler.py embedding(). # ONLY CHANGE: Added timeout=timeout to the client.post() call. # Source: litellm/llms/cohere/embed/handler.py::embedding def _patched_embedding( # type: ignore[return] # noqa: ANN202 diff --git a/unstract/sdk1/uv.lock b/unstract/sdk1/uv.lock index 57561cc209..72d1702ed4 100644 --- a/unstract/sdk1/uv.lock +++ b/unstract/sdk1/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = "==3.12.*" [[package]] @@ -50,7 +50,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.12.15" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -61,25 +61,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } +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/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" }, - { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" }, - { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" }, - { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" }, - { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" }, - { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" }, - { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" }, - { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" }, + { 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" }, ] [[package]] @@ -385,14 +385,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] @@ -573,21 +573,21 @@ wheels = [ [[package]] name = "fastuuid" -version = "0.13.5" +version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/80/3c16a1edad2e6cd82fbd15ac998cc1b881f478bf1f80ca717d941c441874/fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1", size = 18255, upload-time = "2025-09-26T09:05:38.281Z" } +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/21/36/434f137c5970cac19e57834e1f7680e85301619d49891618c00666700c61/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a", size = 494638, upload-time = "2025-09-26T09:14:58.695Z" }, - { url = "https://files.pythonhosted.org/packages/ca/3c/083de2ac007b2b305523b9c006dba5051e5afd87a626ef1a39f76e2c6b82/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7", size = 253138, upload-time = "2025-09-26T09:13:33.283Z" }, - { url = "https://files.pythonhosted.org/packages/73/5e/630cffa1c8775db526e39e9e4c5c7db0c27be0786bb21ba82c912ae19f63/fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202", size = 244521, upload-time = "2025-09-26T09:14:40.682Z" }, - { url = "https://files.pythonhosted.org/packages/4d/51/55d78705f4fbdadf88fb40f382f508d6c7a4941ceddd7825fafebb4cc778/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e", size = 271557, upload-time = "2025-09-26T09:15:09.75Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2b/1b89e90a8635e5587ccdbbeb169c590672ce7637880f2c047482a0359950/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854", size = 272334, upload-time = "2025-09-26T09:07:48.865Z" }, - { url = "https://files.pythonhosted.org/packages/0c/06/4c8207894eeb30414999e5c3f66ac039bc4003437eb4060d8a1bceb4cc6f/fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86", size = 290594, upload-time = "2025-09-26T09:12:54.124Z" }, - { url = "https://files.pythonhosted.org/packages/50/69/96d221931a31d77a47cc2487bdfacfb3091edfc2e7a04b1795df1aec05df/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952", size = 452835, upload-time = "2025-09-26T09:14:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/25/ef/bf045f0a47dcec96247497ef3f7a31d86ebc074330e2dccc34b8dbc0468a/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9", size = 468225, upload-time = "2025-09-26T09:13:38.585Z" }, - { url = "https://files.pythonhosted.org/packages/30/46/4817ab5a3778927155a4bde92540d4c4fa996161ec8b8e080c8928b0984e/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3", size = 444907, upload-time = "2025-09-26T09:14:30.163Z" }, - { url = "https://files.pythonhosted.org/packages/80/27/ab284117ce4dc9b356a7196bdbf220510285f201d27f1f078592cdc8187b/fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85", size = 145415, upload-time = "2025-09-26T09:08:59.494Z" }, - { url = "https://files.pythonhosted.org/packages/f4/0c/f970a4222773b248931819f8940800b760283216ca3dda173ed027e94bdd/fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a", size = 150840, upload-time = "2025-09-26T09:13:46.115Z" }, + { 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" }, ] [[package]] @@ -950,14 +950,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.5.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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -1030,7 +1030,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1038,9 +1038,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -1103,8 +1103,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -1119,6 +1119,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -1625,7 +1629,7 @@ wheels = [ [[package]] name = "openai" -version = "2.16.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1637,9 +1641,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/55/13/17e87641b89b74552ed408a92b231283786523edddc95f3545809fab673c/openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", size = 658717, upload-time = "2026-02-24T20:02:07.958Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94", size = 1120122, upload-time = "2026-02-24T20:02:05.669Z" }, ] [[package]] @@ -1947,7 +1951,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.9" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1955,34 +1959,38 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } +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/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, + { 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.33.2" +version = "2.41.5" 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/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" }, +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/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]] @@ -2523,45 +2531,47 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] name = "tokenizers" -version = "0.22.1" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123, upload-time = "2025-09-19T09:49:23.424Z" } +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/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318, upload-time = "2025-09-19T09:49:11.848Z" }, - { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478, upload-time = "2025-09-19T09:49:09.759Z" }, - { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994, upload-time = "2025-09-19T09:48:56.701Z" }, - { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141, upload-time = "2025-09-19T09:48:59.749Z" }, - { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049, upload-time = "2025-09-19T09:49:05.868Z" }, - { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730, upload-time = "2025-09-19T09:49:01.832Z" }, - { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560, upload-time = "2025-09-19T09:49:03.867Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221, upload-time = "2025-09-19T09:49:07.664Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569, upload-time = "2025-09-19T09:49:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599, upload-time = "2025-09-19T09:49:16.639Z" }, - { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862, upload-time = "2025-09-19T09:49:19.146Z" }, - { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250, upload-time = "2025-09-19T09:49:21.501Z" }, - { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003, upload-time = "2025-09-19T09:49:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684, upload-time = "2025-09-19T09:49:24.953Z" }, + { 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]] @@ -2643,14 +2653,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" 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" } +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/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" }, + { 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]] @@ -2770,7 +2780,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -2786,7 +2796,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/unstract/tool-registry/tool_registry_config/public_tools.json b/unstract/tool-registry/tool_registry_config/public_tools.json index c174d3b948..98cc10d02c 100644 --- a/unstract/tool-registry/tool_registry_config/public_tools.json +++ b/unstract/tool-registry/tool_registry_config/public_tools.json @@ -5,7 +5,7 @@ "schemaVersion": "0.0.1", "displayName": "File Classifier", "functionName": "classify", - "toolVersion": "0.0.79", + "toolVersion": "0.0.80", "description": "Classifies a file into a bin based on its contents", "input": { "description": "File to be classified" @@ -106,9 +106,9 @@ "properties": {} }, "icon": "\n\n \n \n \n \n \n \n \n \n \n \n \n\n", - "image_url": "docker:unstract/tool-classifier:0.0.79", + "image_url": "docker:unstract/tool-classifier:0.0.80", "image_name": "unstract/tool-classifier", - "image_tag": "0.0.79" + "image_tag": "0.0.80" }, "text_extractor": { "tool_uid": "text_extractor", @@ -116,7 +116,7 @@ "schemaVersion": "0.0.1", "displayName": "Text Extractor", "functionName": "text_extractor", - "toolVersion": "0.0.75", + "toolVersion": "0.0.76", "description": "The Text Extractor is a powerful tool designed to convert documents to its text form or Extract texts from documents", "input": { "description": "Document" @@ -191,8 +191,8 @@ } }, "icon": "\n\n \n \n \n \n \n \n \n \n \n \n \n\n", - "image_url": "docker:unstract/tool-text-extractor:0.0.75", + "image_url": "docker:unstract/tool-text-extractor:0.0.76", "image_name": "unstract/tool-text-extractor", - "image_tag": "0.0.75" + "image_tag": "0.0.76" } } diff --git a/unstract/tool-registry/uv.lock b/unstract/tool-registry/uv.lock index 0fb8a3f516..c535fc378b 100644 --- a/unstract/tool-registry/uv.lock +++ b/unstract/tool-registry/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.11.16" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -24,36 +24,38 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/d9/1c4721d143e14af753f2bf5e3b681883e1f24b592c0482df6fa6e33597fa/aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8", size = 7676826, upload-time = "2025-04-02T02:17:44.74Z" } +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/db/38/100d01cbc60553743baf0fba658cb125f8ad674a8a771f765cdc155a890d/aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27", size = 704881, upload-time = "2025-04-02T02:16:09.26Z" }, - { url = "https://files.pythonhosted.org/packages/21/ed/b4102bb6245e36591209e29f03fe87e7956e54cb604ee12e20f7eb47f994/aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713", size = 464564, upload-time = "2025-04-02T02:16:10.781Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e1/a9ab6c47b62ecee080eeb33acd5352b40ecad08fb2d0779bcc6739271745/aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb", size = 456548, upload-time = "2025-04-02T02:16:12.764Z" }, - { url = "https://files.pythonhosted.org/packages/80/ad/216c6f71bdff2becce6c8776f0aa32cb0fa5d83008d13b49c3208d2e4016/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321", size = 1691749, upload-time = "2025-04-02T02:16:14.304Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ea/7df7bcd3f4e734301605f686ffc87993f2d51b7acb6bcc9b980af223f297/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e", size = 1736874, upload-time = "2025-04-02T02:16:16.538Z" }, - { url = "https://files.pythonhosted.org/packages/51/41/c7724b9c87a29b7cfd1202ec6446bae8524a751473d25e2ff438bc9a02bf/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c", size = 1786885, upload-time = "2025-04-02T02:16:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/86/b3/f61f8492fa6569fa87927ad35a40c159408862f7e8e70deaaead349e2fba/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce", size = 1698059, upload-time = "2025-04-02T02:16:20.234Z" }, - { url = "https://files.pythonhosted.org/packages/ce/be/7097cf860a9ce8bbb0e8960704e12869e111abcd3fbd245153373079ccec/aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e", size = 1626527, upload-time = "2025-04-02T02:16:22.092Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1d/aaa841c340e8c143a8d53a1f644c2a2961c58cfa26e7b398d6bf75cf5d23/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b", size = 1644036, upload-time = "2025-04-02T02:16:23.707Z" }, - { url = "https://files.pythonhosted.org/packages/2c/88/59d870f76e9345e2b149f158074e78db457985c2b4da713038d9da3020a8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540", size = 1685270, upload-time = "2025-04-02T02:16:25.874Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b1/c6686948d4c79c3745595efc469a9f8a43cab3c7efc0b5991be65d9e8cb8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b", size = 1650852, upload-time = "2025-04-02T02:16:27.556Z" }, - { url = "https://files.pythonhosted.org/packages/fe/94/3e42a6916fd3441721941e0f1b8438e1ce2a4c49af0e28e0d3c950c9b3c9/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e", size = 1704481, upload-time = "2025-04-02T02:16:29.573Z" }, - { url = "https://files.pythonhosted.org/packages/b1/6d/6ab5854ff59b27075c7a8c610597d2b6c38945f9a1284ee8758bc3720ff6/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c", size = 1735370, upload-time = "2025-04-02T02:16:31.191Z" }, - { url = "https://files.pythonhosted.org/packages/73/2a/08a68eec3c99a6659067d271d7553e4d490a0828d588e1daa3970dc2b771/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71", size = 1697619, upload-time = "2025-04-02T02:16:32.873Z" }, - { url = "https://files.pythonhosted.org/packages/61/d5/fea8dbbfb0cd68fbb56f0ae913270a79422d9a41da442a624febf72d2aaf/aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2", size = 411710, upload-time = "2025-04-02T02:16:34.525Z" }, - { url = "https://files.pythonhosted.org/packages/33/fb/41cde15fbe51365024550bf77b95a4fc84ef41365705c946da0421f0e1e0/aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682", size = 438012, upload-time = "2025-04-02T02:16:36.103Z" }, + { 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" }, ] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload-time = "2024-12-13T17:10:40.86Z" } +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/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" }, + { 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]] @@ -598,14 +600,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.5.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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -656,7 +658,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -664,9 +666,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -698,8 +700,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -714,6 +716,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -1139,7 +1145,7 @@ wheels = [ [[package]] name = "openai" -version = "2.16.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1151,9 +1157,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/55/13/17e87641b89b74552ed408a92b231283786523edddc95f3545809fab673c/openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", size = 658717, upload-time = "2026-02-24T20:02:07.958Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94", size = 1120122, upload-time = "2026-02-24T20:02:05.669Z" }, ] [[package]] @@ -1360,7 +1366,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1368,34 +1374,38 @@ dependencies = [ { 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" } +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/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { 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.33.2" +version = "2.41.5" 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/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" }, +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/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]] @@ -1699,43 +1709,47 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] name = "tokenizers" -version = "0.15.2" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91", size = 320256, upload-time = "2024-02-12T02:28:50.62Z" } +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/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670", size = 2574444, upload-time = "2024-02-12T02:25:27.417Z" }, - { url = "https://files.pythonhosted.org/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51", size = 2411608, upload-time = "2024-02-12T02:25:29.74Z" }, - { url = "https://files.pythonhosted.org/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98", size = 3652367, upload-time = "2024-02-12T02:25:32.079Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66", size = 3529509, upload-time = "2024-02-12T02:25:34.042Z" }, - { url = "https://files.pythonhosted.org/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd", size = 3396516, upload-time = "2024-02-12T02:25:35.884Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38", size = 3918811, upload-time = "2024-02-12T02:25:37.85Z" }, - { url = "https://files.pythonhosted.org/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c", size = 4025494, upload-time = "2024-02-12T02:25:40.247Z" }, - { url = "https://files.pythonhosted.org/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456", size = 3575314, upload-time = "2024-02-12T02:25:42.212Z" }, - { url = "https://files.pythonhosted.org/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834", size = 9682779, upload-time = "2024-02-12T02:25:44.027Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d", size = 9995614, upload-time = "2024-02-12T02:25:46.804Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b", size = 2011030, upload-time = "2024-02-12T02:25:49.829Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221", size = 2180523, upload-time = "2024-02-12T02:25:51.542Z" }, + { 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]] @@ -1752,11 +1766,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.13.1" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633, upload-time = "2025-04-03T16:11:20.955Z" } +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/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739, upload-time = "2025-04-03T16:11:19.281Z" }, + { 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]] @@ -1774,14 +1788,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } +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/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, + { 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]] @@ -1883,7 +1897,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -1899,7 +1913,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/uv.lock b/uv.lock index b58c60183f..1d7419fb3d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = "==3.12.*" [[package]] @@ -50,7 +50,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.5" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -61,25 +61,25 @@ dependencies = [ { 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" } +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/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/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" }, ] [[package]] @@ -510,14 +510,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.3" +version = "8.1.8" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] @@ -1396,14 +1396,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "9.0.0" +version = "8.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -1512,7 +1512,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.26.0" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1520,9 +1520,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -1573,8 +1573,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -1589,6 +1589,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -2073,7 +2077,7 @@ wheels = [ [[package]] name = "openai" -version = "2.34.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2085,9 +2089,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/89/f1e78f5f828f4e97a6ebca8f45c6b35667da12b074ac490dc8362b882279/openai-2.34.0.tar.gz", hash = "sha256:828b4efcbb126352c2b5eb97d33ae890c92a71ab72511aefc1b7fe64aeccb07b", size = 759556, upload-time = "2026-05-04T17:34:08.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/13/17e87641b89b74552ed408a92b231283786523edddc95f3545809fab673c/openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673", size = 658717, upload-time = "2026-02-24T20:02:07.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/40/f090499f10514515081d09cb9da09f25b821eb20497e9423afe4f07b4ecf/openai-2.34.0-py3-none-any.whl", hash = "sha256:c996a71b1a210f3569844572ad4c609307e978515fb76877cf449b72596e549e", size = 1316535, upload-time = "2026-05-04T17:34:06.773Z" }, + { url = "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94", size = 1120122, upload-time = "2026-02-24T20:02:05.669Z" }, ] [[package]] @@ -2466,7 +2470,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.13.3" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2474,39 +2478,38 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" } +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/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" }, + { 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.46.3" +version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" }, - { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" }, - { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" }, - { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" }, - { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" }, - { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" }, - { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" }, - { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" }, - { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" }, - { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" }, - { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" }, - { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" }, +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/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]] @@ -3219,20 +3222,21 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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" } +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/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/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" }, ] [[package]] @@ -3246,29 +3250,28 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.23.1" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/60/21f715d9faba5f5407ff759472ade058ec4a507ad62bcea47cb847239a73/tokenizers-0.23.1.tar.gz", hash = "sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa", size = 365748, upload-time = "2026-04-27T14:43:25.606Z" } +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/87/39/b87a87d5bb9470610b80a2d31df42fcffeaf35118b8b97952b2aff598cc7/tokenizers-0.23.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224", size = 3146732, upload-time = "2026-04-27T14:43:15.427Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6a/068ed9f6e444c9d7e9d55ce134181325700f3d7f30410721bdc8f848d727/tokenizers-0.23.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e", size = 3054954, upload-time = "2026-04-27T14:43:13.745Z" }, - { url = "https://files.pythonhosted.org/packages/6c/36/e006edf031154cba92b8416057d92c3abe3635e4c4b0aa0b5b9bb39dde70/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3", size = 3374081, upload-time = "2026-04-27T14:43:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ef/7735d226f9c7f874a6bee5e3f27fb25ecabdf207d37b8cf45286d0795893/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6", size = 3247641, upload-time = "2026-04-27T14:43:03.856Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d9/24827036f6e21297bfffda0768e58eb6096a4f411e932964a01707857931/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959", size = 3585624, upload-time = "2026-04-27T14:43:10.664Z" }, - { url = "https://files.pythonhosted.org/packages/0c/9a/22f3582b3a4f49358293a5206e25317621ee4526bfe9cdaa0f07a12e770e/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51", size = 3844062, upload-time = "2026-04-27T14:43:05.643Z" }, - { url = "https://files.pythonhosted.org/packages/7e/65/b8f8814eef95800f20721384136d9a1d22241d50b2874357cb70542c392f/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a", size = 3460098, upload-time = "2026-04-27T14:43:08.854Z" }, - { url = "https://files.pythonhosted.org/packages/0d/d5/1353e5f677ec27c2494fb6a6725e82d56c985f53e90ec511369e7e4f02c6/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4", size = 3346235, upload-time = "2026-04-27T14:43:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/71/89/39b6b8fc073fb6d413d0147aa333dc7eff7be65639ac9d19930a0b21bf33/tokenizers-0.23.1-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a", size = 3426398, upload-time = "2026-04-27T14:43:07.264Z" }, - { url = "https://files.pythonhosted.org/packages/0f/80/127c854da64827e5b79264ce524993a90dddcb320e5cd42412c5c02f9e8a/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e", size = 9823279, upload-time = "2026-04-27T14:43:17.222Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ba/44c2502feb1a058f096ddfb4e0996ef3225a01a388e1a9b094e91689fe93/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288", size = 9644986, upload-time = "2026-04-27T14:43:19.45Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c1/464019a9fb059870bfe4eebb4ba12208f3042035e258bf5e782906bd3847/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4", size = 9976181, upload-time = "2026-04-27T14:43:21.463Z" }, - { url = "https://files.pythonhosted.org/packages/79/94/3ac1432bda31626071e9b6a12709b97ae05131c804b94c8f3ac622c5da32/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96", size = 10113853, upload-time = "2026-04-27T14:43:23.617Z" }, - { url = "https://files.pythonhosted.org/packages/6a/dd/631b21433c771b1382535326f0eca80b9c9cee2e64961dd993bc9ac4669e/tokenizers-0.23.1-cp310-abi3-win32.whl", hash = "sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948", size = 2536263, upload-time = "2026-04-27T14:43:29.888Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/2553f72aaf65a2797d4229e37fa7fbe38ffbf3e32912d31bdd78b3323e59/tokenizers-0.23.1-cp310-abi3-win_amd64.whl", hash = "sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7", size = 2798223, upload-time = "2026-04-27T14:43:28.51Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2b/2be299bab55fc595e3d38567edb1a87f86e594842968fa9515a07bdcf422/tokenizers-0.23.1-cp310-abi3-win_arm64.whl", hash = "sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9", size = 2664127, upload-time = "2026-04-27T14:43:26.949Z" }, + { 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]] @@ -3294,7 +3297,7 @@ wheels = [ [[package]] name = "typer" -version = "0.25.1" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -3302,9 +3305,9 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" }, ] [[package]] @@ -3719,7 +3722,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -3735,7 +3738,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "unstract/core" }, ] provides-extras = ["aws", "azure", "gcs"] diff --git a/workers/uv.lock b/workers/uv.lock index 4d540d78bc..35a0699bff 100644 --- a/workers/uv.lock +++ b/workers/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.12" resolution-markers = [ "python_full_version >= '3.14'", @@ -696,14 +696,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.1" +version = "8.1.8" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] @@ -1745,14 +1745,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.1" +version = "8.5.0" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304, upload-time = "2024-09-11T14:56:08.937Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514, upload-time = "2024-09-11T14:56:07.019Z" }, ] [[package]] @@ -1891,7 +1891,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.26.0" +version = "4.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1899,9 +1899,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" }, ] [[package]] @@ -1993,8 +1993,8 @@ wheels = [ [[package]] name = "litellm" -version = "1.82.3" -source = { git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3#809ba8ce35f1c763eb28717a82f1079b5c8f151d" } +version = "1.83.10" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -2009,6 +2009,10 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/e2/16/fc51935e406887079f0d7c20427b9a15b9fc1d558e68b4e7072331b70db4/litellm-1.83.10.tar.gz", hash = "sha256:d54eaa98f93a1eb02decf593dbb525fa1ddd4cf03686c1d5c7bb69c2a9ba2a41", size = 14726546, upload-time = "2026-04-19T02:36:28.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/58/8dd69d5b1ab11f206a9c9f21b6fd191bcdd4fb3ed90b8efbf3a1291fd47c/litellm-1.83.10-py3-none-any.whl", hash = "sha256:55203a7b5551efec8f2fccde29ee045ba057e768591e0b6b9fe1d12f00685ff8", size = 16334780, upload-time = "2026-04-19T02:36:25.274Z" }, +] [[package]] name = "llama-cloud" @@ -4383,26 +4387,49 @@ wheels = [ [[package]] name = "tiktoken" -version = "0.9.0" +version = "0.12.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/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" }, +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]] @@ -4463,7 +4490,7 @@ wheels = [ [[package]] name = "typer" -version = "0.24.1" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -4471,9 +4498,9 @@ dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" }, ] [[package]] @@ -4722,7 +4749,7 @@ requires-dist = [ { name = "gcsfs", marker = "extra == 'gcs'", specifier = "~=2024.10.0" }, { name = "httpx", specifier = ">=0.25.2" }, { name = "jsonschema" }, - { name = "litellm", git = "https://github.com/Zipstack/litellm.git?tag=v1.82.3" }, + { name = "litellm", specifier = "==1.83.10" }, { name = "llama-index", specifier = ">=0.14.13" }, { name = "llama-index-vector-stores-milvus", specifier = ">=0.9.6" }, { name = "llama-index-vector-stores-pinecone", specifier = ">=0.7.1" }, @@ -4738,7 +4765,7 @@ requires-dist = [ { name = "redis", specifier = ">=5.2.1" }, { name = "s3fs", extras = ["boto3"], marker = "extra == 'aws'", specifier = "~=2024.10.0" }, { name = "singleton-decorator", specifier = "~=1.0.0" }, - { name = "tiktoken", specifier = "~=0.9.0" }, + { name = "tiktoken", specifier = "~=0.12.0" }, { name = "unstract-core", editable = "../unstract/core" }, ] provides-extras = ["aws", "azure", "gcs"] From a7a035a18cb885955ec8894b671f26bb4102fcb7 Mon Sep 17 00:00:00 2001 From: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com> Date: Thu, 21 May 2026 15:31:59 +0530 Subject: [PATCH 09/10] UN-3476 [FIX] Revert atomic wrap on set_user_organization (#1977) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The atomic wrap from #1954 uncommits the new org row when frictionless_onboarding HTTP-calls the LLMW portal mid-transaction. The portal runs on a separate DB session and under READ COMMITTED cannot see the uncommitted row, so the call returns 400 and the caller silently persists an adapter with an empty unstract_key. Every new signup since 2026-05-19 09:47 UTC ships a broken free-trial X2Text adapter (401 on first OCR). Hotfix only — Phase 2 (UN-3476) restructures the function so the atomic guarantee is reapplied around just the pure-DB writes, with HTTP and non-DB side effects moved outside the transaction. Co-authored-by: Claude Opus 4.7 (1M context) --- backend/account_v2/authentication_controller.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/account_v2/authentication_controller.py b/backend/account_v2/authentication_controller.py index d27bb50656..cf1c245f69 100644 --- a/backend/account_v2/authentication_controller.py +++ b/backend/account_v2/authentication_controller.py @@ -3,7 +3,6 @@ from django.conf import settings from django.contrib.auth import logout as django_logout -from django.db import transaction from django.db.utils import IntegrityError from django.http import HttpRequest from django.middleware import csrf @@ -164,7 +163,6 @@ def user_organizations(self, request: Request) -> Any: return response - @transaction.atomic def set_user_organization(self, request: Request, organization_id: str) -> Response: user: User = request.user new_organization = False From 093a6b46274d6b2d5d0fde0873c557e47f53034a Mon Sep 17 00:00:00 2001 From: harini-venkataraman Date: Tue, 26 May 2026 15:06:18 +0530 Subject: [PATCH 10/10] Restore text_extractor tool removed in Phase 5 decommission The Phase 5 decommission commit removed classifier, structure, text_extractor, and prompt-service. However, text_extractor is still in active use by customers. This surgically restores only the text_extractor tool while keeping the other decommissions in place. - Restore tools/text_extractor/ directory (14 files from origin/main) - Add tool-text_extractor back to docker-compose.build.yaml - Add tool-text-extractor back to docker-tools-build-push.yaml workflow Co-Authored-By: Claude Opus 4.6 --- .../workflows/docker-tools-build-push.yaml | 4 + docker/docker-compose.build.yaml | 5 + tools/text_extractor/.dockerignore | 3 + tools/text_extractor/.gitignore | 162 ++++++++++++++++++ tools/text_extractor/Dockerfile | 42 +++++ tools/text_extractor/README.md | 114 ++++++++++++ tools/text_extractor/__init__.py | 0 tools/text_extractor/requirements.txt | 9 + tools/text_extractor/sample.env | 14 ++ tools/text_extractor/src/config/icon.svg | 53 ++++++ .../text_extractor/src/config/properties.json | 53 ++++++ .../src/config/runtime_variables.json | 18 ++ tools/text_extractor/src/config/spec.json | 7 + .../src/example_package/__init__.py | 0 tools/text_extractor/src/main.py | 109 ++++++++++++ tools/text_extractor/tests/__init__.py | 0 16 files changed, 593 insertions(+) create mode 100644 tools/text_extractor/.dockerignore create mode 100644 tools/text_extractor/.gitignore create mode 100644 tools/text_extractor/Dockerfile create mode 100644 tools/text_extractor/README.md create mode 100644 tools/text_extractor/__init__.py create mode 100644 tools/text_extractor/requirements.txt create mode 100644 tools/text_extractor/sample.env create mode 100644 tools/text_extractor/src/config/icon.svg create mode 100644 tools/text_extractor/src/config/properties.json create mode 100644 tools/text_extractor/src/config/runtime_variables.json create mode 100644 tools/text_extractor/src/config/spec.json create mode 100644 tools/text_extractor/src/example_package/__init__.py create mode 100644 tools/text_extractor/src/main.py create mode 100644 tools/text_extractor/tests/__init__.py diff --git a/.github/workflows/docker-tools-build-push.yaml b/.github/workflows/docker-tools-build-push.yaml index 01039ad345..8bb2ed2d18 100644 --- a/.github/workflows/docker-tools-build-push.yaml +++ b/.github/workflows/docker-tools-build-push.yaml @@ -14,6 +14,7 @@ on: type: choice options: # Define available options - tool-sidecar + - tool-text-extractor add_latest_tag: description: "Also tag as 'latest'" required: false @@ -58,6 +59,9 @@ jobs: if [ "${{ github.event.inputs.service_name }}" == "tool-sidecar" ]; then echo "context=." >> $GITHUB_OUTPUT echo "dockerfile=docker/dockerfiles/tool-sidecar.Dockerfile" >> $GITHUB_OUTPUT + elif [ "${{ github.event.inputs.service_name }}" == "tool-text-extractor" ]; then + echo "context=." >> $GITHUB_OUTPUT + echo "dockerfile=./tools/text_extractor/Dockerfile" >> $GITHUB_OUTPUT fi # Determine tags based on inputs diff --git a/docker/docker-compose.build.yaml b/docker/docker-compose.build.yaml index db3b02c4e5..8431138d2d 100644 --- a/docker/docker-compose.build.yaml +++ b/docker/docker-compose.build.yaml @@ -30,6 +30,11 @@ services: build: dockerfile: docker/dockerfiles/x2text.Dockerfile context: .. + tool-text_extractor: + image: unstract/tool-text_extractor:${VERSION} + build: + dockerfile: tools/text_extractor/Dockerfile + context: .. # Unified worker image (replaces all individual worker images) worker-unified: image: unstract/worker-unified:${VERSION} diff --git a/tools/text_extractor/.dockerignore b/tools/text_extractor/.dockerignore new file mode 100644 index 0000000000..c26352afcc --- /dev/null +++ b/tools/text_extractor/.dockerignore @@ -0,0 +1,3 @@ +venv/ +.venv/ +.env diff --git a/tools/text_extractor/.gitignore b/tools/text_extractor/.gitignore new file mode 100644 index 0000000000..3a8816c9ee --- /dev/null +++ b/tools/text_extractor/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__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 +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.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 + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm-project.org/#use-with-ide +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# 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 +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/tools/text_extractor/Dockerfile b/tools/text_extractor/Dockerfile new file mode 100644 index 0000000000..92274d3812 --- /dev/null +++ b/tools/text_extractor/Dockerfile @@ -0,0 +1,42 @@ +FROM python:3.12-slim-trixie + +LABEL maintainer="Zipstack Inc." + +ENV \ + # Extended PYTHONPATH to include all unstract module source directories + APP_HOME=/app \ + BUILD_PACKAGES_PATH=unstract \ + # Increase timeout for large packages (flipt-client is ~45MB) + PIP_DEFAULT_TIMEOUT=120 + +# Install dependencies for unstructured library's partition +RUN apt-get update && apt-get --no-install-recommends -y install dumb-init libmagic-dev poppler-utils\ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install --no-cache-dir -U pip +# Set the working directory in the container +WORKDIR ${APP_HOME} +COPY tools/text_extractor/requirements.txt /app/ + +# Copy specific subdirectories while preserving structure +COPY ${BUILD_PACKAGES_PATH}/sdk1 /unstract/sdk1 +COPY ${BUILD_PACKAGES_PATH}/core /unstract/core +COPY ${BUILD_PACKAGES_PATH}/flags /unstract/flags + +RUN pip install --no-cache-dir -r requirements.txt && \ + pip install --no-cache-dir \ + opentelemetry-distro \ + opentelemetry-exporter-otlp \ + platformdirs>=3.0.0 \ + && pip install opentelemetry-instrumentation-openai \ + && opentelemetry-bootstrap -a install \ + && pip uninstall -y opentelemetry-instrumentation-openai-v2 + + +# Copy the contents of your project directory into the container at /app +COPY tools/text_extractor/src /app/src/ +WORKDIR /app/src + + +ENTRYPOINT ["opentelemetry-instrument", "python", "main.py"] diff --git a/tools/text_extractor/README.md b/tools/text_extractor/README.md new file mode 100644 index 0000000000..65ef08437a --- /dev/null +++ b/tools/text_extractor/README.md @@ -0,0 +1,114 @@ +# Text Extractor Tool + +The Text Extractor Tool is a powerful tool designed to extract text from documents. +In other words, it converts documents into their text versions. +For example, it can convert PDF to text, image to text, etc. + +## Required Environment Variables + +| Variable | Description | +| -------------------------- |----------------------------------------------------------------------------| +| `PLATFORM_SERVICE_HOST` | The host where the platform service is running | +| `PLATFORM_SERVICE_PORT` | The port where the service is listening | +| `PLATFORM_SERVICE_API_KEY` | The API key for the platform | +| `EXECUTION_DATA_DIR` | The directory in the filesystem which contains contents for tool execution | +| `X2TEXT_HOST` | The host where the x2text service is running | +| `X2TEXT_PORT` | The port where the x2text service is listening | + +## Setting Up a Dev Environment + +1. Setup a virtual environment and activate it: + +```commandline +python -m venv .venv +source .venv/bin/activate +``` +2. Install the dependencies for the tool. + + Two Options + - Install by Pypi version + ```commandline + pip install -r requirements.txt + ``` + - To use the local development version of the [unstract-sdk](https://pypi.org/project/unstract-sdk/) install it from the local repository. + Replace the path with the path to your local repository + ```commandline + pip install -e ~/path_to_repo/sdks/. + ``` + +#### Tool execution preparation + +1. Load the environment variables for the tool. +Make a copy of the `sample.env` file and name it `.env`. Fill in the required values. +They get loaded with [python-dotenv](https://pypi.org/project/python-dotenv/) through the SDK. + +2. Update the tool's `data_dir` marked by the `EXECUTION_DATA_DIR` env. This has to be done before each tool execution since the tool updates the `INFILE` and `METADATA.json`. + +#### Run SPEC command + +Represents the JSON schema for the runtime configurable `settings` of a tool +```commandline +python main.py --command SPEC +``` + +#### Run PROPERTIES command + +Describes some metadata for the tool such as its `version`, `description`, `inputs` and `outputs` +```commandline +python main.py --command PROPERTIES +``` + +#### Run ICON command + +Returns the SVG icon for the tool, used by Unstract's frontend +```commandline +python main.py --command ICON +``` + +#### Run VARIABLES command + +Represents the runtime variables or envs that will be used by the tool +```commandline +python main.py --command VARIABLES +``` + +#### Run RUN command + + +The schema of the JSON required for settings can be found by running the [SPEC](#run-spec-command) command. Alternatively if you have access to the code base, it is located in the `config` folder as `spec.json`. + +```commandline +python main.py \ + --command RUN \ + --settings '{ + "extractorId": "" + }' \ + --workflow-id '00000000-0000-0000-0000-000000000000' \ + --log-level DEBUG + +``` +### Testing the tool from its docker image + +Build the tool docker image from the folder containing the `Dockerfile` with +```commandline +docker build -t unstract/tool-example:0.0.1 . +``` + +Make sure the directory pointed by `EXECUTION_DATA_DIR` has the required information for the tool to run and +necessary services like the `platform-service` is up. +To test the tool from its docker image, run the following command + +```commandline +docker run -it \ + --network unstract-network \ + --env-file .env \ + -v "$(pwd)"/data_dir:/app/data_dir \ + unstract/tool-example:0.0.1 \ + --command RUN \ + --settings '{ + "extractorId": "" + }' \ + --workflow-id '00000000-0000-0000-0000-000000000000' \ + --log-level DEBUG + +``` diff --git a/tools/text_extractor/__init__.py b/tools/text_extractor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/text_extractor/requirements.txt b/tools/text_extractor/requirements.txt new file mode 100644 index 0000000000..4347f319b3 --- /dev/null +++ b/tools/text_extractor/requirements.txt @@ -0,0 +1,9 @@ +# Add your dependencies here + +# Required for all unstract tools +# aws alone is needed here +# because tools use transient temporary storage. + +-e file:/unstract/core +-e file:/unstract/sdk1[aws] +-e file:/unstract/flags diff --git a/tools/text_extractor/sample.env b/tools/text_extractor/sample.env new file mode 100644 index 0000000000..de3ab3e916 --- /dev/null +++ b/tools/text_extractor/sample.env @@ -0,0 +1,14 @@ +PLATFORM_SERVICE_HOST= +PLATFORM_SERVICE_PORT= +PLATFORM_SERVICE_API_KEY= +EXECUTION_DATA_DIR= +# X2TEXT service +X2TEXT_HOST= +X2TEXT_PORT= + +# File System Configuration for Workflow Execution +# Directory path for execution data storage +# (e.g., bucket/execution/org_id/workflow_id/execution_id) +EXECUTION_DATA_DIR= +# Storage provider for Workflow Execution (e.g., minio, S3) +WORKFLOW_EXECUTION_FILE_STORAGE_CREDENTIALS='{"provider":"minio","credentials"={"endpoint_url":"http://localhost:9000","key":"XXX","secret":"XXX"}}' diff --git a/tools/text_extractor/src/config/icon.svg b/tools/text_extractor/src/config/icon.svg new file mode 100644 index 0000000000..e3a507bafa --- /dev/null +++ b/tools/text_extractor/src/config/icon.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/tools/text_extractor/src/config/properties.json b/tools/text_extractor/src/config/properties.json new file mode 100644 index 0000000000..adb32f269a --- /dev/null +++ b/tools/text_extractor/src/config/properties.json @@ -0,0 +1,53 @@ +{ + "schemaVersion": "0.0.1", + "displayName": "Text Extractor", + "functionName": "text_extractor", + "toolVersion": "0.0.76", + "description": "The Text Extractor is a powerful tool designed to convert documents to its text form or Extract texts from documents", + "input": { + "description": "Document" + }, + "output": { + "description": "structured json output" + }, + "result": { + "type": "TXT", + "description": "Text format of input", + "schema": {} + }, + "adapter": { + "textExtractors": [ + { + "isEnabled": true, + "adapterId": "extractorId", + "title": "Text Extraction Adapter", + "isRequired": true, + "description": "Adapter to extract" + } + ] + }, + "ioCompatibility": { + "api": { + "sourceSupport": true, + "destinationSupport": true, + "additionalArgs": { + "sync": true + } + }, + "file": { + "sourceSupport": true, + "destinationSupport": true, + "additionalArgs": {} + }, + "db": { + "destinationSupport": true, + "additionalArgs": {} + } + }, + "restrictions": { + "maxFileSize": "200MB", + "allowedFileTypes": [ + "*" + ] + } +} diff --git a/tools/text_extractor/src/config/runtime_variables.json b/tools/text_extractor/src/config/runtime_variables.json new file mode 100644 index 0000000000..38177eee0b --- /dev/null +++ b/tools/text_extractor/src/config/runtime_variables.json @@ -0,0 +1,18 @@ +{ + "title": "Runtime Variables", + "description": "Runtime Variables for text extractor", + "type": "object", + "required": [], + "properties": { + "X2TEXT_HOST": { + "type": "string", + "title": "X2Text service host", + "description": "The host where the x2text service is running" + }, + "X2TEXT_PORT": { + "type": "string", + "title": "X2Text service port", + "description": "The port where the x2text service is running" + } + } +} diff --git a/tools/text_extractor/src/config/spec.json b/tools/text_extractor/src/config/spec.json new file mode 100644 index 0000000000..d55b27cf15 --- /dev/null +++ b/tools/text_extractor/src/config/spec.json @@ -0,0 +1,7 @@ +{ + "title": "Text Extractor tool settings", + "description": "Text extraction from documents", + "type": "object", + "required": [], + "properties": {} +} diff --git a/tools/text_extractor/src/example_package/__init__.py b/tools/text_extractor/src/example_package/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/text_extractor/src/main.py b/tools/text_extractor/src/main.py new file mode 100644 index 0000000000..c72dfd5e98 --- /dev/null +++ b/tools/text_extractor/src/main.py @@ -0,0 +1,109 @@ +import ast +import sys +from pathlib import Path +from typing import Any + +from unstract.sdk1.constants import LogState, UsageKwargs +from unstract.sdk1.tool.base import BaseTool +from unstract.sdk1.tool.entrypoint import ToolEntrypoint +from unstract.sdk1.x2txt import TextExtractionResult, X2Text + + +class TextExtractor(BaseTool): + def validate(self, input_file: str, settings: dict[str, Any]) -> None: + """Validate the input file and settings. + + Args: + input_file (str): The path to the input file. + settings (dict[str, Any]): The settings for the tool. + """ + text_extraction_adapter_id = settings["extractorId"] + if not text_extraction_adapter_id: + self.stream_error_and_exit( + "Adaptor id not found, please select adaptor for extractor tool" + ) + + def run( + self, + settings: dict[str, Any], + input_file: str, + output_dir: str, + ) -> None: + """Run the text extraction process. + + Args: + settings (dict[str, Any]): The settings for the tool. + input_file (str): The path to the input file. + output_dir (str): The path to the output directory. + + Raises: + Exception: If there is an error creating/writing the output file. + + Returns: + None + """ + text_extraction_adapter_id = settings["extractorId"] + + self.stream_log( + f"Extractor ID: {text_extraction_adapter_id} " + "has been retrieved from settings." + ) + + input_log = f"Processing file: \n\n`{self.source_file_name}`" + self.stream_update(input_log, state=LogState.INPUT_UPDATE) + + usage_kwargs: dict[Any, Any] = dict() + usage_kwargs[UsageKwargs.RUN_ID] = self.file_execution_id + usage_kwargs[UsageKwargs.FILE_NAME] = self.source_file_name + + text_extraction_adapter = X2Text( + tool=self, + adapter_instance_id=text_extraction_adapter_id, + usage_kwargs=usage_kwargs, + ) + self.stream_log("Text extraction adapter has been created successfully.") + extraction_result: TextExtractionResult = text_extraction_adapter.process( + input_file_path=input_file, fs=self.workflow_filestorage, tags=self.tags + ) + extracted_text = self.convert_to_actual_string(extraction_result.extracted_text) + + self.stream_log("Text has been extracted successfully.") + + first_5_lines = "\n\n".join(extracted_text.split("\n")[:5]) + output_log = f"### Text\n\n```text\n{first_5_lines}\n```\n\n...(truncated)" + self.stream_update(output_log, state=LogState.OUTPUT_UPDATE) + + try: + self.stream_log("Preparing to write the extracted text.") + if self.source_file_name: + output_path = Path(output_dir) / f"{Path(self.source_file_name).stem}.txt" + self.workflow_filestorage.write( + path=output_path, mode="w", data=extracted_text + ) + + self.stream_log("Tool output written successfully.") + else: + self.stream_error_and_exit( + "Error creating/writing output file: source_file_name not found" + ) + except Exception as e: + self.stream_error_and_exit(f"Error creating/writing output file: {e}") + self.write_tool_result(data=extracted_text) + + def convert_to_actual_string(self, text: Any) -> str: + if isinstance(text, bytes): + return text.decode("utf-8") + elif isinstance(text, str): + if text.startswith("b'") and text.endswith("'"): + bytes_text: bytes = ast.literal_eval(text) + return bytes_text.decode("utf-8") + else: + return text + else: + return str(text) + + +if __name__ == "__main__": + args = sys.argv[1:] + tool = TextExtractor.from_tool_args(args=args) + ToolEntrypoint.launch(tool=tool, args=args) diff --git a/tools/text_extractor/tests/__init__.py b/tools/text_extractor/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2