diff --git a/.env.example b/.env.example index 662e53e11..4d4d4fed9 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,19 @@ +# Image Tags +# ---------- + +TAG=dev + # Security # -------- + +# Please set these access keys to something random and unique. +# Note: For just testing, you can set them to the same value. + +# On Linux, you can generate a random key with: +# > openssl rand -base64 32 +# OR +# > tr -dc 'A-Za-z0-9+_/' AGENTS_API_KEY= COZO_AUTH_TOKEN= @@ -8,25 +22,38 @@ LITELLM_POSTGRES_PASSWORD= LITELLM_MASTER_KEY= LITELLM_SALT_KEY= LITELLM_REDIS_PASSWORD= +MAX_FREE_SESSIONS=50 +MAX_FREE_EXECUTIONS=50 -# Memory Store -# ----------- +# LLM Providers +# -------------- -# COZO_HOST=http://memory-store:9070 -# COZO_PORT=9070 -# COZO_ROCKSDB_DIR=cozo.db -# COZO_BACKUP_DIR=/backup -# COZO_MNT_DIR=/data +### Recommended LLM Providers -# Gateway -# ------ +# OPENAI_API_KEY= +# VOYAGE_API_KEY= -# GATEWAY_PORT=80 -# TRAEFIK_LOG_LEVEL=INFO +# HUGGING_FACE_HUB_TOKEN= +# ANTHROPIC_API_KEY= +# OPENROUTER_API_KEY= +# GROQ_API_KEY= +# GEMINI_API_KEY= +# CLOUDFLARE_API_KEY= +# CLOUDFLARE_ACCOUNT_ID= +# NVIDIA_NIM_API_KEY= +# GITHUB_API_KEY= +# GOOGLE_APPLICATION_CREDENTIALS=.keys/julep-vertexai-svc.json # Agents API # --------- +### Embedding Model +### > Set to either "voyage/voyage-3" or "Alibaba-NLP/gte-large-en-v1.5" +### > Use Alibaba-NLP/gte-large-en-v1.5 with local embedding server + +# EMBEDDING_MODEL_ID=voyage/voyage-3 +# EMBEDDING_MODEL_ID=Alibaba-NLP/gte-large-en-v1.5 + # AGENTS_API_HOSTNAME=localhost # AGENTS_API_PROTOCOL=http # AGENTS_API_KEY_HEADER_NAME=Authorization @@ -34,9 +61,26 @@ LITELLM_REDIS_PASSWORD= # TRUNCATE_EMBED_TEXT=true # WORKER_URL=temporal:7233 # AGENTS_API_DEBUG=false -# EMBEDDING_MODEL_ID=Alibaba-NLP/gte-large-en-v1.5 # NUM_GPUS=1 # INTEGRATION_SERVICE_URL=http://integrations:8000 +# USE_BLOB_STORE_FOR_TEMPORAL=false +# BLOB_STORE_CUTOFF_KB=1024 +# BLOB_STORE_BUCKET=agents-api + +# Memory Store +# ----------- + +# COZO_HOST=http://memory-store:9070 +# COZO_PORT=9070 +# COZO_ROCKSDB_DIR=cozo.db +# COZO_BACKUP_DIR=/backup +# COZO_MNT_DIR=/data + +# Gateway +# ------ + +# GATEWAY_PORT=80 +# TRAEFIK_LOG_LEVEL=INFO # Temporal # -------- @@ -46,6 +90,7 @@ LITELLM_REDIS_PASSWORD= # TEMPORAL_WORKER_URL=temporal:7233 # TEMPORAL_POSTGRES_DB=temporal # TEMPORAL_POSTGRES_USER=temporal +# TEMPORAL_SCHEDULE_TO_CLOSE_TIMEOUT=3600 # LiteLLM # ------- @@ -56,16 +101,14 @@ LITELLM_REDIS_PASSWORD= # LITELLM_REDIS_HOST=litellm-redis # LITELLM_REDIS_PORT=6379 -# LLM Providers -# -------------- +# Blob Store +# ----------- -# OPENAI_API_KEY= -# HUGGING_FACE_HUB_TOKEN= -# ANTHROPIC_API_KEY= -# GROQ_API_KEY= -# CLOUDFLARE_API_KEY= -# CLOUDFLARE_ACCOUNT_ID= -# NVIDIA_NIM_API_KEY= -# GITHUB_API_KEY= -# VOYAGE_API_KEY= -# GOOGLE_APPLICATION_CREDENTIALS=.keys/julep-vertexai-svc.json +# S3_ENDPOINT=http://seaweedfs:8333 +# S3_ACCESS_KEY= +# S3_SECRET_KEY= + +# Integrations Service +# ------------ + +# INTEGRATIONS_SERVICE_PORT=8000 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..882584ad3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,27 @@ +name: "🐛 Bug Report" +description: "Submit a bug report to help us improve" +title: "[Bug]: " +labels: ["bug"] +body: + - type: textarea + id: description + attributes: + label: "📜 Description" + placeholder: "A clear and concise description of what the bug is." + + - type: textarea + id: steps-to-reproduce + attributes: + label: "👟 Reproduction steps" + placeholder: "1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error" + + - type: checkboxes + id: no-duplicate-issues + attributes: + label: "👀 Have you searched previous issues to see if this has been raised before?" + options: + - label: "I checked and didn't find similar issue" + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..4e472d981 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Have Question? + url: https://discord.com/invite/JTSBGRZrzj + about: Join Official Discord server \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..147471178 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,29 @@ +name: 🚀 Feature +description: "Submit a proposal for a new feature" +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: feature-description + validations: + required: true + attributes: + label: "🔖 Feature description" + placeholder: "A clear and concise description of what the feature is." + - type: textarea + id: pitch + attributes: + label: "🎤 Why is this feature needed ?" + placeholder: "Please explain why this feature should be implemented and how it would be used. Add examples, if applicable." + - type: textarea + id: solution + attributes: + label: "✌️ How do you aim to achieve this?" + placeholder: "A clear and concise description of what you want to happen." + - type: checkboxes + id: no-duplicate-issues + attributes: + label: "👀 Have you searched issues and PRs to see if this feature request has been raised before?" + options: + - label: "I checked and didn't find similar issue" + required: true diff --git a/.github/ISSUE_TEMPLATE/refactor.yml b/.github/ISSUE_TEMPLATE/refactor.yml new file mode 100644 index 000000000..107c8fdd9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/refactor.yml @@ -0,0 +1,17 @@ +name: "🛠️ Refactor" +description: "Submit a bug report to help us improve" +title: "[Refactor]: " +labels: ["refactor"] +body: + - type: textarea + id: description + attributes: + label: "📜 Description" + placeholder: "A clear and concise description of what should be refactored." + + - type: textarea + id: relevant-files + attributes: + label: "👟 Relevant files" + placeholder: "1. `agents-api/Dockerfile` needs to change + 2. `integrations/**/*.py` files need to be reformatted" diff --git a/.github/workflows/doctoc-on-push.yml b/.github/workflows/doctoc-on-push.yml index c0dfdf2c6..1281fd9cf 100644 --- a/.github/workflows/doctoc-on-push.yml +++ b/.github/workflows/doctoc-on-push.yml @@ -11,4 +11,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAX_HEADER_LEVEL: 3 TOC_TITLE: '

📖 Table of Contents

' - FOLDING: true + FOLDING: false + CREATE_PR: true + CHECK_ONLY_DEFAULT_BRANCH: true diff --git a/.github/workflows/generate-openapi-code-from-typespec b/.github/workflows/generate-openapi-code-from-typespec new file mode 100644 index 000000000..7f4b6569e --- /dev/null +++ b/.github/workflows/generate-openapi-code-from-typespec @@ -0,0 +1,59 @@ +name: Generate OpenAPI code +run-name: ${{ github.actor }} is generating openapi code + +on: + pull_request: + paths: + - 'typespec/**' + push: + paths: + - 'typespec/**' + +jobs: + Typecheck: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - uses: actions/setup-node@v4 + with: + node-version: 'latest' + cache: npm + cache-dependency-path: 'typespec/package-lock.json' + + - name: Install libboost + run: sudo apt-get install -y libboost-all-dev + + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + + - name: Configure Poetry to use .venv + run: | + cd agents-api + poetry config virtualenvs.in-project true + + - name: Cache Poetry virtualenv + uses: actions/cache@v4 + with: + path: agents-api/.venv + key: ${{ runner.os }}-agents-api-poetry-${{ hashFiles('agents-api/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-agents-api-poetry- + + - name: Install dependencies + run: | + cd agents-api + poetry install + + - name: Generate openapi code + run: | + bash scripts/generate_openapi_code.sh + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} diff --git a/.github/workflows/lint-agents-api-pr.yml b/.github/workflows/lint-agents-api-pr.yml index 3721a8b59..43945a3ef 100644 --- a/.github/workflows/lint-agents-api-pr.yml +++ b/.github/workflows/lint-agents-api-pr.yml @@ -20,6 +20,9 @@ jobs: with: python-version: "3.12" + - name: Install libboost + run: sudo apt-get install -y libboost-all-dev + - name: Install and configure Poetry uses: snok/install-poetry@v1 diff --git a/.github/workflows/lint-integrations-service-pr.yml b/.github/workflows/lint-integrations-service-pr.yml new file mode 100644 index 000000000..7d2f8c566 --- /dev/null +++ b/.github/workflows/lint-integrations-service-pr.yml @@ -0,0 +1,57 @@ +name: Lint integrations-service +run-name: ${{ github.actor }} is linting the code + +on: + pull_request: + paths: + - 'integrations-service/**' + push: + paths: + - 'integrations-service/**' + +jobs: + Lint-And-Format: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + + - name: Configure Poetry to use .venv + run: | + cd integrations-service + poetry config virtualenvs.in-project true + + - name: Cache Poetry virtualenv + uses: actions/cache@v4 + with: + path: integrations-service/.venv + key: ${{ runner.os }}-integrations-service-poetry-${{ hashFiles('integrations-service/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-integrations-service-poetry- + + - name: Install dependencies + run: | + cd integrations-service + poetry install + + - name: Lint and format + run: | + cd integrations-service + poetry run poe format + poetry run poe lint + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "refactor: Lint integrations-service (CI)" + branch: ${{ github.head_ref }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} \ No newline at end of file diff --git a/.github/workflows/test-agents-api-pr.yml b/.github/workflows/test-agents-api-pr.yml index 95e676657..a93e7eb72 100644 --- a/.github/workflows/test-agents-api-pr.yml +++ b/.github/workflows/test-agents-api-pr.yml @@ -20,6 +20,9 @@ jobs: with: python-version: "3.12" + - name: Install libboost + run: sudo apt-get install -y libboost-all-dev + - name: Install and configure Poetry uses: snok/install-poetry@v1 @@ -44,7 +47,7 @@ jobs: - name: Run tests run: | cd agents-api - poetry run poe test --fail-limit 1 + poetry run poe test --fail-limit 3 concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/test-integrations-service-pr.yml b/.github/workflows/test-integrations-service-pr.yml new file mode 100644 index 000000000..118e8a13c --- /dev/null +++ b/.github/workflows/test-integrations-service-pr.yml @@ -0,0 +1,50 @@ +name: Test integrations-service +run-name: ${{ github.actor }} is testing the code + +on: + pull_request: + paths: + - 'integrations-service/**' + push: + paths: + - 'integrations-service/**' + +jobs: + Test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + + - name: Configure Poetry to use .venv + run: | + cd integrations-service + poetry config virtualenvs.in-project true + + - name: Cache Poetry virtualenv + uses: actions/cache@v4 + with: + path: integrations-service/.venv + key: ${{ runner.os }}-integrations-service-poetry-${{ hashFiles('integrations-service/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-integrations-service-poetry- + + - name: Install dependencies + run: | + cd integrations-service + poetry install + + - name: Run tests + run: | + cd integrations-service + poetry run poe test + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/translate-readme.yml b/.github/workflows/translate-readme.yml new file mode 100644 index 000000000..f6b28b370 --- /dev/null +++ b/.github/workflows/translate-readme.yml @@ -0,0 +1,40 @@ +name: Translate ReadME + +on: + push: + paths: + - "README.md" + +jobs: + readme-translator: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python v3.10.12 + uses: actions/setup-python@v5 + with: + python-version: "3.10.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install deep-translator git+https://github.com/Jwink3101/parmapper + + - name: Run translator script + run: python scripts/readme_translator.py + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(readme): translate README.md" + title: "Translate README.md" + body: "This PR updates the translated versions of README.md" + branch: "translate-readme" + delete-branch: true + add-paths: | + README.md + README-*.md \ No newline at end of file diff --git a/.github/workflows/typecheck-agents-api-pr.yml b/.github/workflows/typecheck-agents-api-pr.yml index 9fbc5d95c..c8e23e19b 100644 --- a/.github/workflows/typecheck-agents-api-pr.yml +++ b/.github/workflows/typecheck-agents-api-pr.yml @@ -20,6 +20,9 @@ jobs: with: python-version: "3.12" + - name: Install libboost + run: sudo apt-get install -y libboost-all-dev + - name: Install and configure Poetry uses: snok/install-poetry@v1 diff --git a/.gitignore b/.gitignore index 7d1d2ca75..0adb06f10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# BECAUSE DIWANK IS OBSESSED +*gaga* .DS_Store *.swp ngrok* diff --git a/.gitmodules b/.gitmodules index 0f808e530..6e454d8f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ -[submodule "sdks/node-sdk"] - path = sdks/node-sdk - url = https://github.com/julep-ai/node-sdk.git +[submodule "code-interpreter/vendor/cohere-ai/cohere-terrarium"] + path = code-interpreter/vendor/cohere-ai/cohere-terrarium + url = https://github.com/cohere-ai/cohere-terrarium.git [submodule "sdks/python-sdk"] path = sdks/python-sdk url = https://github.com/julep-ai/python-sdk.git +[submodule "sdks/node-sdk"] + path = sdks/node-sdk + url = https://github.com/julep-ai/node-sdk.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 80bdebaed..b4e401ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,288 +1,15 @@ - -# [v1.0.0-alpha1](https://github.com/julep-ai/julep/releases/tag/v1.0.0-alpha1) - 30 Sep 2024 +# TODO: Update changelog +# SCRUM-29 -## What's Changed -* doc: v1.0 docs by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/527 -* feat: Update README social banner and bake-push-to-hub.yml by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/537 +# Changelog +All notable changes to this project will be documented in this file. -**Full Changelog**: https://github.com/julep-ai/julep/compare/v0.4.1...v1.0.0-alpha1 +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -[Changes][v1.0.0-alpha1] +## [Unreleased] +### Added - -# [v0.4.1](https://github.com/julep-ai/julep/releases/tag/v0.4.1) - 28 Sep 2024 - -## What's Changed -* Update changelog for v0.4.0 by [@github-actions](https://github.com/github-actions) in https://github.com/julep-ai/julep/pull/531 -* fix: Bake on release as well by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/532 - -## New Contributors -* [@github-actions](https://github.com/github-actions) made their first contribution in https://github.com/julep-ai/julep/pull/531 - -**Full Changelog**: https://github.com/julep-ai/julep/compare/v0.4.0...v0.4.1 - -[Changes][v0.4.1] - - - -# [v0.4.0](https://github.com/julep-ai/julep/releases/tag/v0.4.0) - 28 Sep 2024 - -## What's Changed -* feat(agents-api): Add cozo migrations for tasks schema by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/349 -* feat(agents-api): updated openapi schema for tasks spec by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/350 -* Update README.md by [@ijindal1](https://github.com/ijindal1) in https://github.com/julep-ai/julep/pull/384 -* Update README.md by [@ijindal1](https://github.com/ijindal1) in https://github.com/julep-ai/julep/pull/385 -* feat(agents-api): cozo queries for tasks by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/352 -* fix(sdks/ts): Fixed bugs, updated sdk by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/390 -* feat(agents-api): Toy implementation of Tasks by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/391 -* feat(agents-api): Adaptive context (via recursive summarization) by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/306 -* chore(deps-dev): bump braces from 3.0.2 to 3.0.3 in /sdks/ts in the npm_and_yarn group across 1 directory by [@dependabot](https://github.com/dependabot) in https://github.com/julep-ai/julep/pull/394 -* v/0.3.9 by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/395 -* feat(tasks): Enable all fields of ExecutionInput by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/396 -* feat(tasks): Update execution transition relation by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/397 -* fix: Handle prompt too big error by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/401 -* feat(sdks/ts): Add adaptive context options to the SDK by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/404 -* feat(sdks/ts): Add runtime validations for TS SDK by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/425 -* refactor(agents-api): Rework routers to split routes into individual files by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/422 -* support for `Hermes-2-Theta-Llama-3-8B` as default OSS model by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/424 -* fix: Add exception handler for litellm API error by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/432 -* fix(openapi): Fix mistakes in openapi.yaml file by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/434 -* fix: Apply various small fixes to task execution logic by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/436 -* Codegen for all new typespec types by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/435 -* feat(agents-api): New chat context query and model by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/437 -* feat(agents-api): Add get_transition query by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/438 -* chore: Disable all the routes except of tasks and agents by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/439 -* fix(agents-api): Fix execution input query by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/440 -* f/wait for input step by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/441 -* feat(agents-api): Implement doc* models by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/442 -* Reimplement tests for queries and operations by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/443 -* feat(agents-api): Hybrid docs search by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/444 -* feat(agents-api): Add temporal workflow lookup relation and queries by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/446 -* feat: new routes for docs, users, search by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/445 -* feat(agents-api): Add litellm proxy to docker compose by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/448 -* fix(agents-api): Fixed tests for models and queries by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/449 -* creatorrr/f add missing tests by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/450 -* feat(agents-api): Add route tests by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/451 -* Add tests for docs routes by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/452 -* feat(agents-api): Preliminary implementation of session.chat by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/453 -* feat(agents-api): Make chat route tests pass by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/454 -* feat(agents-api): Add some workflow tests by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/456 -* fix(agents-api): Minor fix to tests by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/457 -* f/task tests by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/458 -* feat(agents-api): ALL TESTS PASS!! :D by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/459 -* feat: Add test for evaluate step by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/460 -* fix(agents-api): Fix typespec for foreach step and regenerate by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/461 -* fix(agents-api): Fix the typespec bug and regenerate by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/462 -* x/fix codec by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/463 -* feat(agents-api): Add YAML support by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/464 -* refactor(agents-api): Minor refactors to typespec types by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/465 -* fix(agents-api): Fix map reduce step and activity by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/466 -* fix(agents-api): Make the sample work by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/467 -* Dev tasks by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/447 -* fix: Build cozo-bin docker image directly from cozo repo by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/469 -* feat(memory-store): Add backup cronjob by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/470 -* fix: Fix deployment docker compose and move temporal into separate service by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/471 -* fix(agents-api): Fix prompt step by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/472 -* fix(agents-api): Fix task execution logic by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/473 -* Fix create agent tool by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/474 -* fix(agents-api): Fix bug in task-execution workflow and uuid-int-list-to-str fn by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/476 -* fix(agents-api): Fix prompt render, codec and task execution by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/478 -* fix(agents-api): Fix more render issues, execution logic by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/479 -* x/fix task execution by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/482 -* text-embeddings-inference-cpu temp fix for Apple Silicon CPUs by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/480 -* fix(agents-api): Fix task execution logical errors by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/483 -* feat(agents-api): Transitions stream SSE endpoint by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/485 -* feat(agents-api): Set/get steps based on workflow state by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/484 -* Scrum 22 [FIX] agents api list agent tools is returning an empty list by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/487 -* fix(agents-api,typespec): Fix chat/entry typespec models to include tool_calls by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/489 -* fix: Change log-step to a jinja template rather than a simpleeval expression by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/488 -* feat(agents-api): Add parallelism option to map-reduce step by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/490 -* fix: Fix import by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/494 -* misc fixes by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/495 -* build(deps): Bump the npm_and_yarn group across 2 directories with 2 updates by [@dependabot](https://github.com/dependabot) in https://github.com/julep-ai/julep/pull/493 -* hotfix: Apply a temp hotfix for sessions.chat by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/497 -* hotfix(agents-api): Fix session.situation not being rendered by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/499 -* feat: Add agent tools to completion data before sending to litellm in prompt by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/498 -* dev -> main by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/502 -* fix(agents-api): fix create_agent and create_or_update_agent query by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/503 -* fix(llm-proxy): Update docker image to main-v1.46.2 by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/504 -* Add custom api key support to chat endpoint by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/507 -* fix(agents-api): Fix doc recall using search by text by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/506 -* docs: update api.md by [@eltociear](https://github.com/eltociear) in https://github.com/julep-ai/julep/pull/508 -* fix: Get PostgreSQL settings via env vars by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/509 -* main <- dev by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/511 -* Vertex AI client by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/510 -* fix: Retry worker on runtime errors by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/512 -* dev -> main by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/514 -* Implement ToolCallStep & Fix transition after PromptStep by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/513 -* doc: Update README.md with announcement update by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/517 -* feat: Add basic support for integration tools to ToolStep by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/519 -* feat(integration-service): Add integrations service by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/520 -* feat(agents-api,integrations): Working integrations for tool-call step by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/521 -* fix(agents-api): Fix wait for input step by [@HamadaSalhab](https://github.com/HamadaSalhab) in https://github.com/julep-ai/julep/pull/522 -* feat(agents-api): Add support for reading setup args from metadata and Upgrade to python 3.12 by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/525 -* feat: Add docker bake builder by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/528 -* fix: Minor fix to docker bake github actions by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/529 -* feat: Add changelog from release notes by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/530 - -## New Contributors -* [@ijindal1](https://github.com/ijindal1) made their first contribution in https://github.com/julep-ai/julep/pull/384 -* [@HamadaSalhab](https://github.com/HamadaSalhab) made their first contribution in https://github.com/julep-ai/julep/pull/474 -* [@eltociear](https://github.com/eltociear) made their first contribution in https://github.com/julep-ai/julep/pull/508 - -**Full Changelog**: https://github.com/julep-ai/julep/compare/v0.3.4...v0.4.0 - -[Changes][v0.4.0] - - - -# [v0.3.4](https://github.com/julep-ai/julep/releases/tag/v0.3.4) - 31 May 2024 - -## What's Changed -* feat: Implement filtering by metadata by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/337 -* session.chat working without specifying user by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/338 -* feat: Cache generated responses by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/336 -* Add docs-text-embeddings-inference service to deploy/docker-compose.yml by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/339 -* add missing ports and volumes in deploy/docker-compose by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/341 -* feat: Add launching entry point by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/334 -* feat: Add an explicit platform declaration to the services in deploy/docker-compose.yml by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/342 -* [wip] feat(agents-api,sdks): multimodal support by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/343 -* fix(sdks/ts): Fix codegen issue by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/374 -* Version 0.3.4 by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/375 -* fix: Convert messages content to JSON by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/376 -* fix: Convert IDs to UUID before passing to query executing functions by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/377 -* fix(agents-api): fix fn calling and local deployment by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/378 -* feat: Improve typehints by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/379 - - -**Full Changelog**: https://github.com/julep-ai/julep/compare/v0.3.3...v0.3.4 - -[Changes][v0.3.4] - - - -# [v0.3.3](https://github.com/julep-ai/julep/releases/tag/v0.3.3) - 17 May 2024 - -## What's Changed -* fix: Add summarizer source type to the query by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/301 -* fix: Get summarization model form env var and create a corresponsing … by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/302 -* feat: Make docs content be splited by users by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/303 -* feat: litellm for multiple model support by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/304 -* use VALID_MODELS to support JULEP_MODELS by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/308 -* Agent creation fixes by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/309 -* feat: Add new agents docs embbeddings functionality by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/305 -* Fix docs search by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/311 -* Make embeddings asynchronous using temporal by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/332 -* feat: Add doc IDs to the session chat response by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/331 -* chore: Update SDKs by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/333 - - -**Full Changelog**: https://github.com/julep-ai/julep/compare/v0.3.2...v0.3.3 - -[Changes][v0.3.3] - - - -# [v0.3.2](https://github.com/julep-ai/julep/releases/tag/v0.3.2) - 27 Apr 2024 - -- Initial github release - -## What's Changed -* fix(python-sdk): temporarily remove async types by [@philipbalbas](https://github.com/philipbalbas) in https://github.com/julep-ai/julep/pull/196 -* fix: Set empty string as a default value for function description by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/195 -* feat: exception handling for api keys and models by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/197 -* feat: Create and push docker images to hub by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/198 -* fix: Sessions update by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/201 -* fix: Sessions update by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/202 -* feat(sdk): update tools by [@philipbalbas](https://github.com/philipbalbas) in https://github.com/julep-ai/julep/pull/206 -* fix: Merge agent and user metadata by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/204 -* fix: Display agent instructions by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/205 -* fix: Disable model-serving by default by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/207 -* fix: patch fix for function calling by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/203 -* remove model surgery notebooks by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/208 -* feat: Create github action changelog-ci.yml by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/209 -* Sweep: Add a detailed README.md in the memory-store/ directory by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/214 -* fix: Display agent instructions by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/216 -* Sweep: Add a detailed README.md in the examples/ directory by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/215 -* fix: Set metadata to an empty dict by default by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/217 -* Sweep: Update the docstrings and comments in sdks/ts/src/env.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/235 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/memory.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/236 -* Sweep: Update the docstrings and comments in sdks/ts/src/utils/invariant.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/237 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/agent.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/238 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/tool.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/239 -* Sweep: Update the docstrings and comments in sdks/ts/src/utils/isValidUuid4.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/240 -* Sweep: Update the docstrings and comments in sdks/ts/src/client.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/241 -* Sweep: Update the docstrings and comments in sdks/ts/src/utils/requestConstructor.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/242 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/base.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/243 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/user.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/244 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/session.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/245 -* Sweep: Update the docstrings and comments in sdks/ts/src/utils/openaiPatch.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/246 -* Sweep: Update the docstrings and comments in sdks/ts/src/managers/doc.ts to fix any issues and mismatch between the comment and associated code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/247 -* doc(sdks/ts): Generate documentation for the typescript SDK by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/248 -* Sweep: Update the docstrings and comments in sdks/python/julep/env.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/262 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/memory.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/271 -* Sweep: Update the docstrings and comments in sdks/python/julep/utils/openai_patch.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/270 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/base.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/263 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/types.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/269 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/utils.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/268 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/tool.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/267 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/doc.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/266 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/session.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/265 -* Sweep: Update the docstrings and comments in sdks/python/julep/managers/agent.py to fix any issues and mismatch between the comments present and surrounding code by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/264 -* refactor(agents-api): Add decorator to wrap cozo queries inside and execute by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/273 -* feat: update python sdk docs by [@philipbalbas](https://github.com/philipbalbas) in https://github.com/julep-ai/julep/pull/272 -* fix: Set default values for function description and parameters by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/274 -* feat(openapi): Update spec to include role=function + render_templates by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/276 -* docs: update SUMMARY.md by [@philipbalbas](https://github.com/philipbalbas) in https://github.com/julep-ai/julep/pull/281 -* chore(deps): bump fastapi from 0.109.2 to 0.110.1 in /agents-api in the pip group across 1 directory by [@dependabot](https://github.com/dependabot) in https://github.com/julep-ai/julep/pull/280 -* chore(deps): bump idna from 3.6 to 3.7 in /memory-store/backup by [@dependabot](https://github.com/dependabot) in https://github.com/julep-ai/julep/pull/278 -* chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 in /sdks/ts by [@dependabot](https://github.com/dependabot) in https://github.com/julep-ai/julep/pull/277 -* F/update julep docs by [@philipbalbas](https://github.com/philipbalbas) in https://github.com/julep-ai/julep/pull/282 -* fix: Fix queries calls by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/283 -* Sweep: Create CONTRIBUTING.md based on README.md in the root / directory by [@sweep-ai](https://github.com/sweep-ai) in https://github.com/julep-ai/julep/pull/285 -* feat: added docker-compose.yml and .env.example for self-hosting by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/286 -* fix: Set default value for user's about text by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/287 -* fix: Update sessions with given IDs only by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/290 -* F/readme update by [@alt-glitch](https://github.com/alt-glitch) in https://github.com/julep-ai/julep/pull/291 -* feat: Truncate messages by [@whiterabbit1983](https://github.com/whiterabbit1983) in https://github.com/julep-ai/julep/pull/294 -* x/fix python sdk by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/296 -* feat(agents-api): Allow single instructions str for agents by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/297 -* fix: Session.user_id should be optional by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/298 -* feat(agents-api): Add jinja templates support by [@creatorrr](https://github.com/creatorrr) in https://github.com/julep-ai/julep/pull/300 - -## New Contributors -* [@dependabot](https://github.com/dependabot) made their first contribution in https://github.com/julep-ai/julep/pull/280 - -**Full Changelog**: https://github.com/julep-ai/julep/commits/v0.3.2 - -[Changes][v0.3.2] - - - -# [v0.3.0](https://github.com/julep-ai/julep/releases/tag/v0.3.0) - 27 Apr 2024 - - - -[Changes][v0.3.0] - - - -# [v0.2.12](https://github.com/julep-ai/julep/releases/tag/v0.2.12) - 27 Apr 2024 - - - -[Changes][v0.2.12] - - -[v1.0.0-alpha1]: https://github.com/julep-ai/julep/compare/v0.4.1...v1.0.0-alpha1 -[v0.4.1]: https://github.com/julep-ai/julep/compare/v0.4.0...v0.4.1 -[v0.4.0]: https://github.com/julep-ai/julep/compare/v0.3.4...v0.4.0 -[v0.3.4]: https://github.com/julep-ai/julep/compare/v0.3.3...v0.3.4 -[v0.3.3]: https://github.com/julep-ai/julep/compare/v0.3.2...v0.3.3 -[v0.3.2]: https://github.com/julep-ai/julep/compare/v0.3.0...v0.3.2 -[v0.3.0]: https://github.com/julep-ai/julep/compare/v0.2.12...v0.3.0 -[v0.2.12]: https://github.com/julep-ai/julep/tree/v0.2.12 - - +- v0.2 agents platform diff --git a/IDEAS.md b/IDEAS.md new file mode 100644 index 000000000..83d6d58d4 --- /dev/null +++ b/IDEAS.md @@ -0,0 +1,1377 @@ +# Expanded Implementation Scenarios for Julep + +Below are detailed implementation plans for each of the 50 scenarios using Julep's **docs**, **sessions**, **tasks**, and **executions** features. Each scenario includes a complexity rating from **1 (easiest)** to **5 (most complex)**. + +--- + +### 1. Automated Customer Support Agent + +**Implementation Using Julep:** + +- **Docs:** + - Store customer data, FAQs, and troubleshooting guides. + - Integrate CRM documentation for accessing and updating customer information. + +- **Sessions:** + - Create a persistent session for each customer to maintain conversation context. + - Track interaction history to personalize support. + +- **Tasks:** + - Define tasks for handling common inquiries (e.g., order status, billing issues). + - Implement escalation tasks for complex issues that require human intervention. + - Automate ticket creation and update processes. + +- **Executions:** + - Execute tasks based on customer inputs. + - Monitor task executions to ensure timely responses and issue resolutions. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integrating with external CRM systems, handling diverse query types, and maintaining contextual sessions, which increases complexity. + +--- + +### 2. Smart Research Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store access to academic databases and research papers. + - Include summarization templates and research methodologies. + +- **Sessions:** + - Manage user-specific research sessions to track ongoing projects and queries. + - Maintain context for multi-step research tasks. + +- **Tasks:** + - Create tasks for searching databases, summarizing articles, and compiling reports. + - Implement conditional steps based on research findings. + +- **Executions:** + - Execute research tasks sequentially or in parallel. + - Stream execution results to provide real-time updates to the user. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires integration with academic databases, advanced summarization capabilities, and managing complex multi-step workflows. + +--- + +### 3. Personal Finance Manager + +**Implementation Using Julep:** + +- **Docs:** + - Store user financial data, budgeting templates, and investment information. + - Integrate banking API documentation for transaction fetching. + +- **Sessions:** + - Create persistent sessions to track user financial activities over time. + - Maintain context for budgeting goals and financial plans. + +- **Tasks:** + - Define tasks for expense tracking, budget creation, and investment monitoring. + - Automate alerts for budget limits and investment opportunities. + +- **Executions:** + - Execute financial tasks based on user interactions and predefined schedules. + - Monitor executions to provide real-time financial advice and updates. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Needs secure integration with banking APIs, real-time data processing, and robust budgeting logic. + +--- + +### 4. Content Creation Workflow + +**Implementation Using Julep:** + +- **Docs:** + - Store SEO guidelines, content templates, and style guides. + - Include access to keyword research tools. + +- **Sessions:** + - Manage content creation sessions to track progress and drafts. + - Maintain context for ongoing content projects. + +- **Tasks:** + - Create multi-step tasks for topic ideation, content drafting, SEO optimization, and scheduling. + - Integrate tools for grammar checking and SEO analysis. + +- **Executions:** + - Automate the execution of content creation tasks. + - Schedule publishing according to editorial calendars. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves coordinating multiple tools and steps but remains manageable with clear task definitions. + +--- + +### 5. E-commerce Order Processing System + +**Implementation Using Julep:** + +- **Docs:** + - Store product catalogs, inventory data, and order processing guidelines. + - Integrate with shipping provider APIs. + +- **Sessions:** + - Create sessions for each order to track its lifecycle. + - Maintain context for customer preferences and order history. + +- **Tasks:** + - Define tasks for order validation, inventory updates, payment processing, and shipment tracking. + - Automate customer notifications at each stage. + +- **Executions:** + - Execute order processing tasks in sequence. + - Monitor executions to handle exceptions like payment failures or inventory shortages. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires robust integrations with payment gateways, inventory systems, and shipping providers, along with handling various order states. + +--- + +### 6. AI-Powered Personal Trainer + +**Implementation Using Julep:** + +- **Docs:** + - Store workout routines, nutritional plans, and progress tracking templates. + - Include integration details for fitness tracking APIs. + +- **Sessions:** + - Create individual sessions for each user to track their fitness journey. + - Maintain context for user goals and progress. + +- **Tasks:** + - Define tasks for generating personalized workout plans, tracking progress, and adjusting routines. + - Automate reminders and motivational messages. + +- **Executions:** + - Execute fitness tasks based on user inputs and scheduled routines. + - Monitor executions to provide real-time feedback and adjustments. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves personalization and integration with fitness data sources, but achievable with well-defined task workflows. + +--- + +### 7. Automated Email Marketing Campaigns + +**Implementation Using Julep:** + +- **Docs:** + - Store email templates, segmentation criteria, and campaign schedules. + - Integrate with email marketing platforms (e.g., SendGrid, Mailchimp). + +- **Sessions:** + - Manage campaign-specific sessions to track interactions and responses. + - Maintain context for ongoing and past campaigns. + +- **Tasks:** + - Create tasks for email creation, scheduling, sending, and performance analysis. + - Automate A/B testing and content personalization. + +- **Executions:** + - Execute email campaigns based on predefined schedules and triggers. + - Monitor execution performance and adjust strategies accordingly. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with email platforms and managing dynamic content delivery, but is straightforward with clear task definitions. + +--- + +### 8. Intelligent Recruitment Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store job descriptions, candidate profiles, and evaluation criteria. + - Integrate with HR systems and job boards. + +- **Sessions:** + - Create sessions for each recruitment process to track candidate interactions. + - Maintain context for candidate status and feedback. + +- **Tasks:** + - Define tasks for resume screening, interview scheduling, and candidate communications. + - Automate feedback collection and report generation. + +- **Executions:** + - Execute recruitment tasks based on candidate actions and application stages. + - Monitor executions to ensure timely processing and compliance. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Involves complex integrations with HR systems, handling diverse candidate data, and ensuring compliance with recruitment processes. + +--- + +### 9. Smart Home Automation Controller + +**Implementation Using Julep:** + +- **Docs:** + - Store device configurations, automation rules, and user preferences. + - Integrate with smart home device APIs (e.g., Philips Hue, Nest). + +- **Sessions:** + - Manage user-specific sessions to track home automation settings. + - Maintain context for user routines and preferences. + +- **Tasks:** + - Create tasks for device control, routine scheduling, and energy monitoring. + - Automate actions based on triggers like time, occupancy, or environmental changes. + +- **Executions:** + - Execute home automation tasks in real-time or based on schedules. + - Monitor executions to ensure devices respond correctly and adjust settings as needed. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires integration with multiple smart devices and managing dynamic automation rules, increasing system complexity. + +--- + +### 10. Automated Legal Document Analyzer + +**Implementation Using Julep:** + +- **Docs:** + - Store legal templates, compliance guidelines, and case studies. + - Integrate with legal databases and document repositories. + +- **Sessions:** + - Create sessions for each document analysis to track progress and findings. + - Maintain context for specific legal requirements and clauses. + +- **Tasks:** + - Define tasks for document ingestion, key information extraction, compliance checking, and summarization. + - Automate flagging of non-compliant sections and suggest necessary amendments. + +- **Executions:** + - Execute document analysis tasks sequentially or in parallel. + - Monitor executions to ensure accuracy and compliance with legal standards. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Involves advanced natural language processing, integration with legal databases, and ensuring compliance with intricate legal standards. + +--- + +### 11. Personalized Learning Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store educational content, learning paths, and assessment criteria. + - Integrate with educational platforms and resources. + +- **Sessions:** + - Create individual learning sessions to track user progress and preferences. + - Maintain context for personalized learning paths and goals. + +- **Tasks:** + - Define tasks for content recommendation, quiz generation, progress tracking, and feedback provision. + - Automate adjustments to learning paths based on performance. + +- **Executions:** + - Execute learning tasks based on user interactions and progress. + - Monitor executions to provide real-time feedback and adjust learning strategies. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires personalization algorithms, integration with educational content sources, and dynamic adaptation to user progress. + +--- + +### 12. AI-Driven Social Media Manager + +**Implementation Using Julep:** + +- **Docs:** + - Store social media strategies, content calendars, and engagement guidelines. + - Integrate with social media APIs (e.g., Twitter, Facebook, LinkedIn). + +- **Sessions:** + - Manage campaign-specific sessions to track posts, engagements, and analytics. + - Maintain context for ongoing and scheduled campaigns. + +- **Tasks:** + - Create tasks for content creation, scheduling, posting, and performance analysis. + - Automate engagement responses and A/B testing of content. + +- **Executions:** + - Execute social media tasks based on schedules and real-time engagement triggers. + - Monitor executions to optimize performance and adjust strategies. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integration with multiple social media platforms, dynamic content management, and real-time engagement handling. + +--- + +### 13. Automated Travel Itinerary Planner + +**Implementation Using Julep:** + +- **Docs:** + - Store travel guides, destination information, and booking APIs. + - Integrate with flight, hotel, and transportation service APIs. + +- **Sessions:** + - Create travel-specific sessions to track itinerary progress and user preferences. + - Maintain context for personalized travel plans and updates. + +- **Tasks:** + - Define tasks for destination research, booking accommodations and transportation, and itinerary scheduling. + - Automate real-time updates and notifications during trips. + +- **Executions:** + - Execute travel planning tasks based on user inputs and predefined schedules. + - Monitor executions to handle changes and provide timely updates. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with travel service APIs and managing dynamic itinerary changes, which adds moderate complexity. + +--- + +### 14. AI-Powered Inventory Management System + +**Implementation Using Julep:** + +- **Docs:** + - Store inventory data, supplier information, and reordering guidelines. + - Integrate with inventory tracking systems and supplier APIs. + +- **Sessions:** + - Manage inventory sessions to monitor stock levels and reorder statuses. + - Maintain context for inventory forecasts and demand trends. + +- **Tasks:** + - Create tasks for stock monitoring, demand forecasting, automatic reordering, and supplier communication. + - Automate alerts for low stock levels and order confirmations. + +- **Executions:** + - Execute inventory management tasks in real-time or based on schedules. + - Monitor executions to ensure accurate stock levels and timely reorders. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires real-time inventory tracking, predictive analytics for demand forecasting, and reliable integration with supplier systems. + +--- + +### 15. Intelligent Health Monitoring System + +**Implementation Using Julep:** + +- **Docs:** + - Store health metrics templates, medical guidelines, and user health data. + - Integrate with health tracking devices and APIs (e.g., Fitbit, Apple Health). + +- **Sessions:** + - Create sessions for each user to track their health metrics and progress. + - Maintain context for personalized health goals and alerts. + +- **Tasks:** + - Define tasks for data collection, health metric analysis, trend monitoring, and alert notifications. + - Automate health insights and recommendations based on data. + +- **Executions:** + - Execute health monitoring tasks continuously or at scheduled intervals. + - Monitor executions to provide real-time health alerts and advice. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires integration with diverse health tracking devices, real-time data processing, and ensuring data privacy and accuracy. + +--- + +### 16. Automated Content Moderation Tool + +**Implementation Using Julep:** + +- **Docs:** + - Store community guidelines, content policies, and moderation rules. + - Integrate with content platforms (e.g., forums, social media). + +- **Sessions:** + - Manage moderation sessions to track content reviews and decisions. + - Maintain context for specific moderation cases and user histories. + +- **Tasks:** + - Create tasks for content ingestion, automated screening, manual review, and action enforcement. + - Automate flagging of inappropriate content and notifying users of violations. + +- **Executions:** + - Execute content moderation tasks in real-time or batch processing. + - Monitor executions to ensure compliance and handle escalations. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Involves sophisticated content analysis, balancing automation with manual oversight, and ensuring adherence to diverse content policies. + +--- + +### 17. AI-Powered Resume Builder + +**Implementation Using Julep:** + +- **Docs:** + - Store resume templates, industry-specific keywords, and formatting guidelines. + - Integrate with LinkedIn and other professional platforms for data fetching. + +- **Sessions:** + - Create user-specific sessions to track resume building progress. + - Maintain context for personalized content and formatting preferences. + +- **Tasks:** + - Define tasks for data collection, content suggestion, resume formatting, and final export. + - Automate style checks and consistency validations. + +- **Executions:** + - Execute resume building tasks based on user inputs and selections. + - Monitor executions to provide real-time feedback and suggestions. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with professional data sources and implementing dynamic content generation and formatting. + +--- + +### 18. Smart Event Management System + +**Implementation Using Julep:** + +- **Docs:** + - Store event templates, scheduling guidelines, and registration forms. + - Integrate with calendar and email platforms. + +- **Sessions:** + - Manage event-specific sessions to track registrations, schedules, and attendee interactions. + - Maintain context for event updates and follow-ups. + +- **Tasks:** + - Create tasks for event creation, attendee registration, schedule management, and post-event follow-ups. + - Automate reminders, notifications, and feedback collection. + +- **Executions:** + - Execute event management tasks based on schedules and attendee actions. + - Monitor executions to handle registrations and event logistics seamlessly. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves coordinating multiple aspects of event planning, handling real-time registrations, and ensuring smooth execution logistics. + +--- + +### 19. Automated Survey Analyzer + +**Implementation Using Julep:** + +- **Docs:** + - Store survey templates, question types, and analysis methodologies. + - Integrate with survey distribution platforms (e.g., SurveyMonkey, Google Forms). + +- **Sessions:** + - Create sessions for each survey to track responses and analysis progress. + - Maintain context for specific survey objectives and parameters. + +- **Tasks:** + - Define tasks for survey distribution, data collection, sentiment analysis, and report generation. + - Automate data visualization and trend identification. + +- **Executions:** + - Execute survey analysis tasks upon survey completion. + - Monitor executions to provide timely and accurate insights. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with survey platforms and implementing effective data analysis and visualization techniques. + +--- + +### 20. AI-Driven Project Management Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store project templates, task guidelines, and progress tracking tools. + - Integrate with project management platforms (e.g., Jira, Trello). + +- **Sessions:** + - Manage project-specific sessions to track tasks, milestones, and team interactions. + - Maintain context for project goals and progress updates. + +- **Tasks:** + - Create tasks for task breakdown, assignment, progress tracking, and status reporting. + - Automate notifications for deadlines and task completions. + +- **Executions:** + - Execute project management tasks based on project timelines and team inputs. + - Monitor executions to ensure projects stay on track and within scope. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integration with diverse project management tools, handling dynamic task assignments, and ensuring effective progress tracking. + +--- + +### 21. Intelligent Document Summarizer + +**Implementation Using Julep:** + +- **Docs:** + - Store access to large documents, research papers, and reports. + - Include summarization algorithms and templates. + +- **Sessions:** + - Create sessions for each document summarization task. + - Maintain context for document sections and summarization preferences. + +- **Tasks:** + - Define tasks for document ingestion, key point extraction, and summary generation. + - Automate quality checks and user-specific summary adjustments. + +- **Executions:** + - Execute document summarization tasks efficiently. + - Monitor executions to ensure accurate and concise summaries. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires advanced natural language processing capabilities and efficient handling of large document data. + +--- + +### 22. Automated Feedback Collection and Analysis + +**Implementation Using Julep:** + +- **Docs:** + - Store feedback forms, analysis templates, and reporting guidelines. + - Integrate with feedback collection platforms (e.g., Typeform, Google Forms). + +- **Sessions:** + - Manage feedback-specific sessions to track responses and analysis progress. + - Maintain context for feedback sources and analysis objectives. + +- **Tasks:** + - Create tasks for feedback distribution, data collection, sentiment analysis, and insight generation. + - Automate categorization and prioritization of feedback. + +- **Executions:** + - Execute feedback analysis tasks promptly upon data collection. + - Monitor executions to provide actionable insights and improvement strategies. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves integrating with feedback platforms and implementing effective sentiment analysis and categorization. + +--- + +### 23. AI-Powered Language Translator + +**Implementation Using Julep:** + +- **Docs:** + - Store language dictionaries, translation models, and formatting guidelines. + - Integrate with translation APIs (e.g., Google Translate, DeepL). + +- **Sessions:** + - Create translation-specific sessions to track user preferences and translation history. + - Maintain context for ongoing translation projects. + +- **Tasks:** + - Define tasks for text ingestion, language detection, translation processing, and quality assurance. + - Automate post-translation formatting and localization adjustments. + +- **Executions:** + - Execute translation tasks in real-time or batch mode. + - Monitor executions to ensure accuracy and contextual relevance. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with robust translation APIs and handling nuances of different languages and contexts. + +--- + +### 24. Smart Appointment Scheduler + +**Implementation Using Julep:** + +- **Docs:** + - Store scheduling templates, availability guidelines, and notification templates. + - Integrate with calendar platforms (e.g., Google Calendar, Outlook). + +- **Sessions:** + - Manage appointment-specific sessions to track scheduling progress and attendee interactions. + - Maintain context for user availability and preferences. + +- **Tasks:** + - Create tasks for availability checking, meeting scheduling, sending reminders, and handling cancellations. + - Automate conflict detection and resolution. + +- **Executions:** + - Execute scheduling tasks based on user inputs and calendar data. + - Monitor executions to ensure appointments are set correctly and notifications are sent. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves integration with calendar systems and implementing conflict resolution logic, which adds moderate complexity. + +--- + +### 25. Automated Inventory Auditor + +**Implementation Using Julep:** + +- **Docs:** + - Store inventory audit templates, reconciliation guidelines, and reporting formats. + - Integrate with inventory management systems and databases. + +- **Sessions:** + - Create auditing sessions to track audit schedules and findings. + - Maintain context for different inventory categories and audit criteria. + +- **Tasks:** + - Define tasks for data extraction, discrepancy detection, reconciliation processes, and report generation. + - Automate audit scheduling and notification of audit results. + +- **Executions:** + - Execute inventory audit tasks periodically or on-demand. + - Monitor executions to ensure accurate and timely audits. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires reliable data integration and robust discrepancy detection mechanisms to handle complex inventory data. + +--- + +### 26. AI-Driven Competitive Analysis Tool + +**Implementation Using Julep:** + +- **Docs:** + - Store competitor profiles, market analysis frameworks, and data sources. + - Integrate with market research APIs and competitor websites. + +- **Sessions:** + - Manage competitive analysis sessions to track data collection and analysis progress. + - Maintain context for specific market segments and competitive factors. + +- **Tasks:** + - Create tasks for data scraping, trend analysis, SWOT analysis, and report generation. + - Automate the aggregation and visualization of competitive data. + +- **Executions:** + - Execute competitive analysis tasks on a scheduled basis. + - Monitor executions to provide up-to-date insights and strategic recommendations. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves complex data scraping, accurate trend analysis, and maintaining up-to-date competitive insights, increasing overall complexity. + +--- + +### 27. Smart Recipe Generator + +**Implementation Using Julep:** + +- **Docs:** + - Store ingredient databases, recipe templates, and dietary guidelines. + - Integrate with nutrition APIs and grocery databases. + +- **Sessions:** + - Create user-specific sessions to track dietary preferences and past recipes. + - Maintain context for ingredient availability and nutritional goals. + +- **Tasks:** + - Define tasks for ingredient analysis, recipe generation, nutritional calculation, and grocery list creation. + - Automate recipe suggestions based on user inputs and constraints. + +- **Executions:** + - Execute recipe generation tasks in real-time based on user requests. + - Monitor executions to ensure recipe accuracy and adherence to dietary needs. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with nutrition and grocery APIs and implementing intelligent recipe generation logic. + +--- + +### 28. Automated Video Content Creator + +**Implementation Using Julep:** + +- **Docs:** + - Store video script templates, editing guidelines, and publishing schedules. + - Integrate with video editing and hosting platforms (e.g., Adobe Premiere, YouTube). + +- **Sessions:** + - Manage video creation sessions to track script development, editing stages, and publishing. + - Maintain context for ongoing video projects and collaboration. + +- **Tasks:** + - Create tasks for script generation, video editing, thumbnail creation, and publishing. + - Automate content review and approval workflows. + +- **Executions:** + - Execute video creation tasks based on project timelines. + - Monitor executions to ensure timely releases and quality standards. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integration with multiple video tools, managing creative workflows, and ensuring high-quality content production. + +--- + +### 29. AI-Powered News Aggregator + +**Implementation Using Julep:** + +- **Docs:** + - Store news source lists, categorization templates, and summarization guidelines. + - Integrate with news APIs (e.g., NewsAPI, RSS feeds). + +- **Sessions:** + - Create user-specific sessions to track news preferences and reading history. + - Maintain context for personalized news feeds and topics of interest. + +- **Tasks:** + - Define tasks for news scraping, categorization, summarization, and personalization. + - Automate feed generation and delivery based on user preferences. + +- **Executions:** + - Execute news aggregation tasks periodically. + - Monitor executions to ensure timely and relevant news delivery. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires efficient news scraping, accurate categorization, and personalized summarization, but is manageable with clear task workflows. + +--- + +### 30. Intelligent Appointment Follow-Up System + +**Implementation Using Julep:** + +- **Docs:** + - Store follow-up templates, feedback forms, and communication guidelines. + - Integrate with CRM and email platforms. + +- **Sessions:** + - Manage follow-up sessions to track appointments and subsequent communications. + - Maintain context for previous interactions and follow-up actions. + +- **Tasks:** + - Create tasks for sending follow-up emails, collecting feedback, and scheduling future appointments. + - Automate reminder notifications and feedback analysis. + +- **Executions:** + - Execute follow-up tasks based on appointment completions. + - Monitor executions to ensure timely and effective communications. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves integration with CRM systems and implementing automated communication workflows, adding moderate complexity. + +--- + +### 31. Automated Compliance Monitoring Tool + +**Implementation Using Julep:** + +- **Docs:** + - Store regulatory guidelines, compliance checklists, and reporting templates. + - Integrate with internal systems and regulatory databases. + +- **Sessions:** + - Create compliance-specific sessions to track monitoring activities and audit trails. + - Maintain context for various compliance standards and organizational policies. + +- **Tasks:** + - Define tasks for continuous monitoring, policy enforcement, and compliance reporting. + - Automate detection of non-compliant activities and trigger corrective actions. + +- **Executions:** + - Execute compliance monitoring tasks in real-time. + - Monitor executions to ensure ongoing adherence to regulations and standards. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Requires comprehensive integration with organizational systems, robust monitoring mechanisms, and ensuring adherence to multifaceted regulatory requirements. + +--- + +### 32. AI-Powered Personal Shopper + +**Implementation Using Julep:** + +- **Docs:** + - Store product catalogs, user preference data, and recommendation algorithms. + - Integrate with e-commerce APIs (e.g., Amazon, Shopify). + +- **Sessions:** + - Manage shopping sessions to track user preferences and purchase history. + - Maintain context for personalized product recommendations. + +- **Tasks:** + - Create tasks for product suggestion, wishlist management, and deal notifications. + - Automate price comparisons and availability checks. + +- **Executions:** + - Execute personal shopping tasks based on user inputs and behavior. + - Monitor executions to provide timely recommendations and alerts. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integration with multiple e-commerce platforms, implementing personalized recommendation logic, and handling real-time deal tracking. + +--- + +### 33. Smart Content Personalization Engine + +**Implementation Using Julep:** + +- **Docs:** + - Store content variants, personalization rules, and user segmentation data. + - Integrate with website CMS and analytics platforms. + +- **Sessions:** + - Create user-specific sessions to track interactions and preferences. + - Maintain context for personalized content delivery. + +- **Tasks:** + - Define tasks for content analysis, user behavior tracking, and personalized content delivery. + - Automate A/B testing and content optimization based on performance metrics. + +- **Executions:** + - Execute content personalization tasks in real-time. + - Monitor executions to adjust personalization strategies dynamically. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires real-time user behavior tracking, dynamic content delivery, and continuous optimization based on analytics, increasing system complexity. + +--- + +### 34. Automated Debt Collection Agent + +**Implementation Using Julep:** + +- **Docs:** + - Store debt agreements, payment schedules, and communication templates. + - Integrate with financial systems and payment gateways. + +- **Sessions:** + - Manage debt collection sessions to track debtor interactions and payment statuses. + - Maintain context for individual debtors and their payment histories. + +- **Tasks:** + - Create tasks for sending payment reminders, negotiating payment plans, and issuing notifications. + - Automate follow-ups and escalation procedures for delinquent accounts. + +- **Executions:** + - Execute debt collection tasks based on payment statuses and schedules. + - Monitor executions to ensure effective communication and resolution. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves sensitive financial data handling, integration with payment systems, and implementing automated negotiation workflows. + +--- + +### 35. AI-Driven Talent Matching System + +**Implementation Using Julep:** + +- **Docs:** + - Store job descriptions, candidate profiles, and matching criteria. + - Integrate with job boards and professional networking platforms. + +- **Sessions:** + - Create sessions for each matching process to track candidate-job pairings. + - Maintain context for specific job requirements and candidate qualifications. + +- **Tasks:** + - Define tasks for candidate screening, skills matching, and recommendation generation. + - Automate notifications to both candidates and employers regarding match statuses. + +- **Executions:** + - Execute talent matching tasks based on incoming job postings and candidate applications. + - Monitor executions to ensure accurate and timely matches. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Requires sophisticated matching algorithms, integration with diverse data sources, and handling dynamic job and candidate data. + +--- + +### 36. Intelligent Expense Reporting Tool + +**Implementation Using Julep:** + +- **Docs:** + - Store expense categories, reimbursement policies, and reporting templates. + - Integrate with financial systems and expense tracking APIs. + +- **Sessions:** + - Manage expense reporting sessions to track submissions and approvals. + - Maintain context for individual employee expenses and budget limits. + +- **Tasks:** + - Create tasks for expense submission, approval workflows, and reimbursement processing. + - Automate validation checks and compliance with policies. + +- **Executions:** + - Execute expense reporting tasks based on submission triggers and approval workflows. + - Monitor executions to ensure timely reimbursements and policy adherence. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires integration with financial systems, implementing approval workflows, and ensuring compliance with expense policies. + +--- + +### 37. Automated Meeting Minutes Recorder + +**Implementation Using Julep:** + +- **Docs:** + - Store meeting agendas, transcription templates, and summary guidelines. + - Integrate with audio transcription services (e.g., Otter.ai, Google Speech-to-Text). + +- **Sessions:** + - Create meeting-specific sessions to track transcription and summarization progress. + - Maintain context for meeting topics and participant interactions. + +- **Tasks:** + - Define tasks for audio ingestion, transcription, summary generation, and distribution. + - Automate the extraction of action items and key decisions. + +- **Executions:** + - Execute transcription and summarization tasks in real-time or post-meeting. + - Monitor executions to ensure accurate recordings and timely distribution. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires reliable audio transcription integration and effective summarization techniques, but manageable with clear task definitions. + +--- + +### 38. AI-Driven Content Recommendation System + +**Implementation Using Julep:** + +- **Docs:** + - Store user profiles, content metadata, and recommendation algorithms. + - Integrate with content management systems and user behavior analytics. + +- **Sessions:** + - Manage user-specific sessions to track interactions and preference changes. + - Maintain context for personalized content delivery. + +- **Tasks:** + - Define tasks for content analysis, user behavior tracking, and recommendation generation. + - Automate personalization based on real-time user interactions. + +- **Executions:** + - Execute content recommendation tasks in real-time. + - Monitor executions to refine recommendation accuracy and relevance. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves real-time data processing, advanced recommendation algorithms, and integration with multiple content sources. + +--- + +### 39. Smart Time Tracking Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store time tracking templates, productivity guidelines, and reporting formats. + - Integrate with productivity tools (e.g., Toggl, Clockify). + +- **Sessions:** + - Create user-specific sessions to track time spent on tasks and projects. + - Maintain context for task prioritization and productivity goals. + +- **Tasks:** + - Define tasks for time logging, productivity analysis, and report generation. + - Automate reminders for time tracking and productivity tips based on usage patterns. + +- **Executions:** + - Execute time tracking tasks continuously or based on user actions. + - Monitor executions to provide real-time productivity insights and suggestions. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with time tracking tools and implementing effective productivity analysis logic. + +--- + +### 40. Automated Webinar Hosting Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store webinar schedules, registration forms, and hosting guidelines. + - Integrate with webinar platforms (e.g., Zoom, WebinarJam). + +- **Sessions:** + - Manage webinar-specific sessions to track registrations, attendee interactions, and follow-ups. + - Maintain context for webinar topics and participant engagement. + +- **Tasks:** + - Create tasks for webinar scheduling, participant management, live interactions, and post-webinar follow-ups. + - Automate reminders, thank-you emails, and feedback collection. + +- **Executions:** + - Execute webinar hosting tasks based on schedules and participant actions. + - Monitor executions to ensure smooth webinar operations and effective follow-ups. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves integration with webinar platforms, managing live interactions, and handling post-event processes seamlessly. + +--- + +### 41. AI-Powered Inventory Forecasting Tool + +**Implementation Using Julep:** + +- **Docs:** + - Store sales data, forecasting models, and inventory guidelines. + - Integrate with sales and inventory tracking systems. + +- **Sessions:** + - Create forecasting sessions to track sales trends and inventory predictions. + - Maintain context for seasonal factors and market conditions affecting inventory. + +- **Tasks:** + - Define tasks for data collection, trend analysis, prediction model execution, and report generation. + - Automate alerts for predicted stock shortages or surpluses. + +- **Executions:** + - Execute forecasting tasks periodically based on sales data updates. + - Monitor executions to refine prediction accuracy and adjust inventory strategies. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires advanced predictive analytics, integration with sales systems, and handling dynamic market conditions influencing inventory. + +--- + +### 42. Smart Contract Management System + +**Implementation Using Julep:** + +- **Docs:** + - Store smart contract templates, execution guidelines, and compliance rules. + - Integrate with blockchain platforms (e.g., Ethereum, Hyperledger). + +- **Sessions:** + - Manage contract-specific sessions to track creation, execution, and monitoring. + - Maintain context for contract terms and participant interactions. + +- **Tasks:** + - Create tasks for contract creation, deployment, execution monitoring, and compliance checks. + - Automate notifications for contract milestones and compliance alerts. + +- **Executions:** + - Execute smart contract tasks based on blockchain events and predefined triggers. + - Monitor executions to ensure contract integrity and compliance. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Involves blockchain integration, ensuring smart contract security, and managing complex execution and compliance workflows. + +--- + +### 43. Automated Knowledge Base Updater + +**Implementation Using Julep:** + +- **Docs:** + - Store knowledge base articles, update guidelines, and categorization rules. + - Integrate with content management systems and information sources. + +- **Sessions:** + - Create knowledge base sessions to track updates, revisions, and user queries. + - Maintain context for content accuracy and relevance. + +- **Tasks:** + - Define tasks for content ingestion, information extraction, categorization, and publishing. + - Automate periodic reviews and updates based on new information sources. + +- **Executions:** + - Execute knowledge base update tasks as new content becomes available. + - Monitor executions to ensure timely and accurate information updates. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires efficient content ingestion, accurate information extraction, and seamless integration with knowledge management systems. + +--- + +### 44. AI-Driven Fraud Detection System + +**Implementation Using Julep:** + +- **Docs:** + - Store fraud detection algorithms, monitoring guidelines, and incident response protocols. + - Integrate with financial transaction systems and security APIs. + +- **Sessions:** + - Manage fraud detection sessions to track suspicious activities and investigations. + - Maintain context for user behavior patterns and anomaly detection. + +- **Tasks:** + - Create tasks for real-time transaction monitoring, anomaly detection, incident logging, and alerting. + - Automate response actions like freezing accounts or notifying security teams. + +- **Executions:** + - Execute fraud detection tasks continuously based on transaction flows. + - Monitor executions to ensure timely detection and response to fraudulent activities. + +**Complexity Rating:** ★★★★★ + +**Explanation:** Involves real-time data processing, sophisticated anomaly detection algorithms, and ensuring robust security measures. + +--- + +### 45. Intelligent Personal Diary Assistant + +**Implementation Using Julep:** + +- **Docs:** + - Store diary templates, emotional analysis guidelines, and reflection prompts. + - Integrate with sentiment analysis APIs. + +- **Sessions:** + - Create user-specific sessions to track daily entries and emotional states. + - Maintain context for personal growth and mood trends. + +- **Tasks:** + - Define tasks for daily entry prompts, sentiment analysis, and insight generation. + - Automate privacy controls and data encryption for secure diary storage. + +- **Executions:** + - Execute diary assistant tasks daily based on user inputs. + - Monitor executions to provide personalized insights and growth tracking. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Requires integration with sentiment analysis tools and ensuring secure data handling, but manageable with well-defined workflows. + +--- + +### 46. Automated Language Learning Tutor + +**Implementation Using Julep:** + +- **Docs:** + - Store language lessons, exercise templates, and feedback guidelines. + - Integrate with language processing APIs and educational resources. + +- **Sessions:** + - Manage learning sessions to track user progress and performance. + - Maintain context for personalized lesson plans and feedback. + +- **Tasks:** + - Create tasks for lesson delivery, exercise generation, progress tracking, and feedback provision. + - Automate adaptive learning paths based on user performance. + +- **Executions:** + - Execute language learning tasks based on user interactions and learning schedules. + - Monitor executions to adjust learning strategies and provide real-time feedback. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves adaptive learning algorithms, integration with language processing tools, and personalized content delivery. + +--- + +### 47. AI-Powered Budgeting Tool for Businesses + +**Implementation Using Julep:** + +- **Docs:** + - Store budgeting templates, financial guidelines, and reporting formats. + - Integrate with accounting systems and financial data sources. + +- **Sessions:** + - Create budgeting sessions to track financial planning and expenditure. + - Maintain context for organizational financial goals and constraints. + +- **Tasks:** + - Define tasks for budget creation, expenditure tracking, financial forecasting, and report generation. + - Automate alerts for budget overruns and financial goal assessments. + +- **Executions:** + - Execute budgeting tasks based on financial data updates and planning cycles. + - Monitor executions to ensure accurate financial tracking and reporting. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires integration with accounting systems, accurate financial forecasting, and robust budgeting logic to handle business complexities. + +--- + +### 48. Smart Compliance Documentation Generator + +**Implementation Using Julep:** + +- **Docs:** + - Store compliance templates, regulatory guidelines, and documentation standards. + - Integrate with regulatory databases and internal policy systems. + +- **Sessions:** + - Manage compliance documentation sessions to track document creation and updates. + - Maintain context for specific regulatory requirements and organizational policies. + +- **Tasks:** + - Create tasks for document generation, compliance checking, format validation, and publishing. + - Automate updates based on regulatory changes and policy revisions. + +- **Executions:** + - Execute compliance documentation tasks as needed or on a schedule. + - Monitor executions to ensure documents meet all compliance standards. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Involves dynamic document generation, adherence to detailed regulatory standards, and ensuring continuous updates based on regulatory changes. + +--- + +### 49. Automated Product Recommendation Engine + +**Implementation Using Julep:** + +- **Docs:** + - Store product catalogs, user behavior data, and recommendation algorithms. + - Integrate with e-commerce platforms and user analytics tools. + +- **Sessions:** + - Create user-specific sessions to track interactions and preferences. + - Maintain context for personalized recommendation accuracy. + +- **Tasks:** + - Define tasks for data collection, behavior analysis, recommendation generation, and user feedback integration. + - Automate real-time recommendations based on user actions and trends. + +- **Executions:** + - Execute recommendation tasks in real-time to provide instant suggestions. + - Monitor executions to refine algorithms and improve recommendation relevance. + +**Complexity Rating:** ★★★★☆ + +**Explanation:** Requires sophisticated recommendation algorithms, real-time data processing, and continuous refinement based on user feedback. + +--- + +### 50. Intelligent Event Feedback Analyzer + +**Implementation Using Julep:** + +- **Docs:** + - Store feedback forms, analysis templates, and reporting standards. + - Integrate with event platforms and feedback collection tools. + +- **Sessions:** + - Manage feedback-specific sessions to track responses and analysis progress. + - Maintain context for event-specific feedback and improvement areas. + +- **Tasks:** + - Create tasks for feedback collection, sentiment analysis, trend identification, and report generation. + - Automate the extraction of actionable insights and improvement suggestions. + +- **Executions:** + - Execute feedback analysis tasks post-event. + - Monitor executions to ensure accurate and timely feedback processing and reporting. + +**Complexity Rating:** ★★★☆☆ + +**Explanation:** Involves integrating with feedback collection tools and implementing effective sentiment analysis and trend identification mechanisms. + +--- + +# Complexity and Difficulty Ratings + +The scenarios have been rated based on the number of integrated features, required integrations, and overall system complexity. Here's a quick overview: + +- **★☆☆☆☆ (1/5): Easiest** +- **★★☆☆☆ (2/5): Low Complexity** +- **★★★☆☆ (3/5): Moderate Complexity** +- **★★★★☆ (4/5): High Complexity** +- **★★★★★ (5/5): Most Complex** + +| **Scenario** | **Complexity Rating** | +|---------------------------------------------------|-----------------------| +| 1. Automated Customer Support Agent | ★★★★☆ | +| 2. Smart Research Assistant | ★★★★☆ | +| 3. Personal Finance Manager | ★★★☆☆ | +| 4. Content Creation Workflow | ★★★☆☆ | +| 5. E-commerce Order Processing System | ★★★★☆ | +| 6. AI-Powered Personal Trainer | ★★★☆☆ | +| 7. Automated Email Marketing Campaigns | ★★★☆☆ | +| 8. Intelligent Recruitment Assistant | ★★★★★ | +| 9. Smart Home Automation Controller | ★★★★☆ | +| 10. Automated Legal Document Analyzer | ★★★★★ | +| 11. Personalized Learning Assistant | ★★★★☆ | +| 12. AI-Driven Social Media Manager | ★★★★☆ | +| 13. Automated Travel Itinerary Planner | ★★★☆☆ | +| 14. AI-Powered Inventory Management System | ★★★★☆ | +| 15. Intelligent Health Monitoring System | ★★★★☆ | +| 16. Automated Content Moderation Tool | ★★★★★ | +| 17. AI-Powered Resume Builder | ★★★☆☆ | +| 18. Smart Event Management System | ★★★★☆ | +| 19. Automated Survey Analyzer | ★★★☆☆ | +| 20. AI-Driven Project Management Assistant | ★★★★☆ | +| 21. Intelligent Document Summarizer | ★★★★☆ | +| 22. Automated Feedback Collection and Analysis | ★★★☆☆ | +| 23. AI-Powered Language Translator | ★★★☆☆ | +| 24. Smart Appointment Scheduler | ★★★☆☆ | +| 25. Automated Inventory Auditor | ★★★★☆ | +| 26. AI-Driven Competitive Analysis Tool | ★★★★☆ | +| 27. Smart Recipe Generator | ★★★☆☆ | +| 28. Automated Video Content Creator | ★★★★☆ | +| 29. AI-Powered News Aggregator | ★★★☆☆ | +| 30. Intelligent Appointment Follow-Up System | ★★★☆☆ | +| 31. Automated Compliance Monitoring Tool | ★★★★★ | +| 32. AI-Powered Personal Shopper | ★★★★☆ | +| 33. Smart Content Personalization Engine | ★★★★☆ | +| 34. Automated Debt Collection Agent | ★★★★☆ | +| 35. AI-Driven Talent Matching System | ★★★★★ | +| 36. Intelligent Expense Reporting Tool | ★★★★☆ | +| 37. Automated Meeting Minutes Recorder | ★★★☆☆ | +| 38. AI-Driven Content Recommendation System | ★★★★☆ | +| 39. Smart Time Tracking Assistant | ★★★☆☆ | +| 40. Automated Webinar Hosting Assistant | ★★★★☆ | +| 41. AI-Powered Inventory Forecasting Tool | ★★★★☆ | +| 42. Smart Contract Management System | ★★★★★ | +| 43. Automated Knowledge Base Updater | ★★★★☆ | +| 44. AI-Driven Fraud Detection System | ★★★★★ | +| 45. Intelligent Personal Diary Assistant | ★★★☆☆ | +| 46. Automated Language Learning Tutor | ★★★★☆ | +| 47. AI-Powered Budgeting Tool for Businesses | ★★★★☆ | +| 48. Smart Compliance Documentation Generator | ★★★★☆ | +| 49. Automated Product Recommendation Engine | ★★★★☆ | +| 50. Intelligent Event Feedback Analyzer | ★★★☆☆ | + +--- + +# Conclusion + +These 50 scenarios showcase the versatility and power of Julep's **docs**, **sessions**, **tasks**, and **executions** features in automating and enhancing various business and personal workflows. Depending on your specific needs and available integrations, these scenarios can be tailored to create efficient, intelligent, and scalable solutions. + +Feel free to explore these scenarios, adapt them to your use cases, and contribute to expanding Julep's capabilities further! \ No newline at end of file diff --git a/README-CN.md b/README-CN.md index d5cb2c2de..3b1cd5220 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,685 +1,1679 @@ -[English](README.md) | 中文 | [日本語](README-JP.md) +[English](README.md) | [中文翻译](README-CN.md) | [日本語翻訳](README-JA.md) | [French](README-FR.md) -
- julep +
+ julep + julep


- 探索文档 + 探索文档(正在开发中) + 探索文档(正在开发中) · - Discord + 不和谐 · 𝕏 · - 领英 + LinkedIn

-

- NPM 版本 + NPM Version   - PyPI - 版本 + PyPI - Version   - Docker 镜像版本 + Docker Image Version   - GitHub 许可证 + GitHub License

-***** +--- -> [!TIP] -> 👨‍💻 来参加 devfest.ai 活动?加入我们的 [Discord](https://discord.com/invite/JTSBGRZrzj) 并查看下方详情。 +> [!注意] +> 从[此处](https://dashboard-dev.julep.ai)获取您的 API 密钥。
-🌟 贡献者和 DevFest.AI 参与者: +贡献🌟(点击展开) -## 🌟 诚邀贡献者! +## 征集贡献者🌟 -我们很高兴欢迎新的贡献者加入 Julep 项目!我们创建了几个"适合新手的问题"来帮助您入门。以下是您可以贡献的方式: +我们很高兴欢迎新贡献者加入 Julep 项目!我们创建了几个“好的第一个问题”来帮助您入门。以下是您可以做出贡献的方式: -1. 查看我们的 [CONTRIBUTING.md](CONTRIBUTING.md) 文件,了解如何贡献的指南。 -2. 浏览我们的[适合新手的问题](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22),找到一个您感兴趣的任务。 -3. 如果您有任何问题或需要帮助,请随时在我们的 [Discord](https://discord.com/invite/JTSBGRZrzj) 频道上联系我们。 +1. 查看我们的 [CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/CONTRIBUTING.md) 文件以获取有关如何贡献的指南。 +2. 浏览我们的 [good first issues](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 以找到您感兴趣的任务。 +3. 如果您有任何疑问或需要帮助,请随时通过我们的 [Discord](https://discord.com/invite/JTSBGRZrzj) 频道联系我们。 -您的贡献,无论大小,对我们都很宝贵。让我们一起创造令人惊叹的东西吧!🚀 +您的贡献,无论大小,对我们来说都是宝贵的。让我们一起创造一些了不起的东西!🚀 -### 🎉 DevFest.AI 2024年10月 +
-激动人心的消息!我们将在整个2024年10月参与 DevFest.AI 活动!🗓️ + + +

📖 目录

+ +- [呼吁贡献者🌟](#call-for-contributors-) +- [简介](#introduction) +- [主要特点](#key-features) +- [快速示例](#quick-example) +- [安装](#安装) +- [Python 快速入门 🐍](#python-quick-start-) +- [Node.js 快速入门🟩](#nodejs-quick-start-) +- [组件](#components) +- [心智模型](#mental-model) +- [概念](#concepts) +- [理解任务](#understanding-tasks) +- [任务的生命周期](#lifecycle-of-a-task) +- [工作流步骤的类型](#types-of-workflow-steps) +- [常见步骤](#common-steps) +- [键值步骤](#key-value-steps) +- [迭代步骤](#iteration-steps) +- [条件步骤](#conditional-steps) +- [其他控制流](#other-control-flow) +- [工具类型](#tool-types) +- [用户定义的`函数`](#user-defined-functions) +- [`系统` 工具](#system-tools) +- [可用的系统资源和操作](#available-system-resources-and-operations) +- [内置 `integrations`](#built-in-integrations) +-[直接`api_calls`](#direct-api_calls) +- [集成](#integrations) +- [其他功能](#other-features) +- [向代理添加工具](#adding-tools-to-agents) +- [管理会话和用户](#managing-sessions-and-users) +- [文档集成与搜索](#document-integration-and-search) +- [参考](#reference) +- [SDK 参考](#sdk-reference) +- [API 参考](#api-reference) +- [本地快速启动](#local-quickstart) +- [Julep 和 LangChain 等有什么区别?](#julep 和 langchain 等之间有什么区别) +- [不同用例](#different-use-cases) +- [不同的外形尺寸](#different-form-factor) +- [总结](#in-summary) -- 在此活动期间为 Julep 做出贡献,有机会赢得超棒的 Julep 周边和礼品!🎁 -- 加入来自世界各地的开发者,为 AI 仓库做出贡献并参与精彩的活动。 -- 非常感谢 DevFest.AI 组织这个fantastic的活动! + -> [!TIP] -> 准备好加入这场盛会了吗?**[发推文开始参与](https://twitter.com/intent/tweet?text=Pumped%20to%20be%20participating%20in%20%40devfestai%20with%20%40julep_ai%20building%20%23ai%20%23agents%20%23workflows%20Let's%20gooo!%20https%3A%2F%2Fgit.new%2Fjulep)**,让我们开始编码吧!🖥️ +## 介绍 + +Julep 是一个用于创建 AI 代理的平台,这些代理可以记住过去的互动并执行复杂的任务。它提供长期记忆并管理多步骤流程。 + +Julep 支持创建多步骤任务,包括决策、循环、并行处理以及与众多外部工具和 API 的集成。 + +尽管许多 AI 应用程序仅限于简单、线性的提示和 API 调用链,并且分支很少,但 Julep 可以处理更复杂的场景,这些场景包括: + +- 有多个步骤, +- 根据模型输出做出决策, +- 产生并行分支, +- 使用多种工具,并且 +- 长时间运行。 + +> [!提示] +> 想象一下,您想要构建一个 AI 代理,它不仅可以回答简单的问题,还可以处理复杂的任务、记住过去的交互,甚至可能使用其他工具或 API。这就是 Julep 的作用所在。阅读 [了解任务](#understanding-tasks) 了解更多信息。 + +## 主要特点 + +1. 🧠 **持久 AI 代理**:在长期交互​​中记住上下文和信息。 +2. 💾 **状态会话**:跟踪过去的互动以获得个性化回应。 +3. 🔄 **多步骤任务**:使用循环和决策构建复杂的多步骤流程。 +4. ⏳ **任务管理**:处理可以无限期运行的长时间运行的任务。 +5.🛠️**内置工具**:在您的任务中使用内置工具和外部 API。 +6. 🔧 **自我修复**:Julep 将自动重试失败的步骤、重新发送消息,并确保您的任务顺利运行。 +7. 📚 **RAG**:使用 Julep 的文档存储构建一个用于检索和使用您自己的数据的系统。 + +![功能](https://github.com/user-attachments/assets/4355cbae-fcbd-4510-ac0d-f8f77b73af70) + +> [!提示] +> Julep 非常适合需要超越简单的提示响应模型的 AI 用例的应用程序。 + +快速示例 + +想象一下一个可以执行以下操作的研究 AI 代理: + +1. **选择一个主题**, +2. 针对该主题提出 30 个搜索查询, +3. 同时进行网页搜索, +4. **总结**结果, +5. 将**摘要发送至 Discord**。 + +> [!注意] +> 在 Julep 中,这将是一项单独的任务80行代码然后运行完全托管全部独立完成。所有步骤都在 Julep 自己的服务器上执行,您无需动手。 + +这是一个有效的例子: + +```yaml +name: Research Agent + +# Optional: Define the input schema for the task +input_schema: + type: object + properties: + topic: + type: string + description: The main topic to research + num_questions: + type: integer + description: The number of search queries to generate + +# Define the tools that the agent can use +tools: + - name: web_search + type: integration + integration: + provider: brave + setup: + api_key: + + - name: discord_webhook + type: api_call + api_call: + url: https://discord.com/api/webhooks// + method: POST + headers: + Content-Type: application/json + +# Special variables: +# - inputs: for accessing the input to the task +# - outputs: for accessing the output of previous steps +# - _: for accessing the output of the previous step + +# Define the main workflow +main: +- prompt: + - role: system + content: >- + You are a research assistant. + Generate {{inputs[0].num_questions|default(30, true)}} diverse search queries related to the topic: + {{inputs[0].topic}} + + Write one query per line. + unwrap: true + +# Evaluate the search queries using a simple python expression +- evaluate: + search_queries: "_.split(NEWLINE)" + +# Run the web search in parallel for each query +- over: "_.search_queries" + map: + tool: web_search + arguments: + query: "_" + parallelism: 5 + +# Collect the results from the web search +- evaluate: + search_results: _ + +# Summarize the results +- prompt: + - role: system + content: > + You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. + The summary should be well-structured, informative, and highlight key findings and insights. Keep the summary concise and to the point. + The length of the summary should be less than 150 words. + Here are the search results: + {{_.search_results}} + unwrap: true + settings: + model: gpt-4o-mini + +- evaluate: + discord_message: |- + f''' + **Research Summary for {inputs[0].topic}** + {_} + ''' + + # Send the summary to Discord +- tool: discord_webhook + arguments: + json_: + content: _.discord_message[:2000] # Discord has a 2000 character limit +``` -![Julep DevFest.AI](https://media.giphy.com/media/YjyUeyotft6epaMHtU/giphy.gif) +在这个例子中,Julep 将自动管理并行执行,重试失败的步骤,重新发送 API 请求,并保持任务可靠运行直到完成。 - +> 这在 30 秒内运行并返回以下输出: - -
-

📖 Table of Contents

- -- [简介](#%E7%AE%80%E4%BB%8B) -- [特性](#%E7%89%B9%E6%80%A7) -- [安装](#%E5%AE%89%E8%A3%85) -- [快速入门指南](#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97) - - [步骤 1:导入 Julep](#%E6%AD%A5%E9%AA%A4-1%E5%AF%BC%E5%85%A5-julep) - - [步骤 2:初始化代理](#%E6%AD%A5%E9%AA%A4-2%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BB%A3%E7%90%86) - - [步骤 3:与代理聊天](#%E6%AD%A5%E9%AA%A4-3%E4%B8%8E%E4%BB%A3%E7%90%86%E8%81%8A%E5%A4%A9) - - [步骤 4:创建多步骤任务](#%E6%AD%A5%E9%AA%A4-4%E5%88%9B%E5%BB%BA%E5%A4%9A%E6%AD%A5%E9%AA%A4%E4%BB%BB%E5%8A%A1) - - [步骤 5:执行任务](#%E6%AD%A5%E9%AA%A4-5%E6%89%A7%E8%A1%8C%E4%BB%BB%E5%8A%A1) -- [概念](#%E6%A6%82%E5%BF%B5) - - [代理](#%E4%BB%A3%E7%90%86) - - [用户](#%E7%94%A8%E6%88%B7) - - [会话](#%E4%BC%9A%E8%AF%9D) - - [任务](#%E4%BB%BB%E5%8A%A1) - - [工具](#%E5%B7%A5%E5%85%B7) - - [文档](#%E6%96%87%E6%A1%A3) - - [执行](#%E6%89%A7%E8%A1%8C) -- [理解任务](#%E7%90%86%E8%A7%A3%E4%BB%BB%E5%8A%A1) - - [工作流步骤类型](#%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AD%A5%E9%AA%A4%E7%B1%BB%E5%9E%8B) -- [高级功能](#%E9%AB%98%E7%BA%A7%E5%8A%9F%E8%83%BD) - - [为代理添加工具](#%E4%B8%BA%E4%BB%A3%E7%90%86%E6%B7%BB%E5%8A%A0%E5%B7%A5%E5%85%B7) - - [管理会话和用户](#%E7%AE%A1%E7%90%86%E4%BC%9A%E8%AF%9D%E5%92%8C%E7%94%A8%E6%88%B7) - - [文档集成和搜索](#%E6%96%87%E6%A1%A3%E9%9B%86%E6%88%90%E5%92%8C%E6%90%9C%E7%B4%A2) -- [SDK 参考](#sdk-%E5%8F%82%E8%80%83) -- [API 参考](#api-%E5%8F%82%E8%80%83) -- [示例和教程](#%E7%A4%BA%E4%BE%8B%E5%92%8C%E6%95%99%E7%A8%8B) -- [贡献](#%E8%B4%A1%E7%8C%AE) -- [支持和社区](#%E6%94%AF%E6%8C%81%E5%92%8C%E7%A4%BE%E5%8C%BA) -- [许可证](#%E8%AE%B8%E5%8F%AF%E8%AF%81) -- [致谢](#%E8%87%B4%E8%B0%A2) +人工智能研究摘要 (点击展开) + +> **人工智能研究摘要** +> +>###人工智能(AI)研究成果摘要 +> +> #### 简介 +> +> 近年来,人工智能 (AI) 领域取得了重大进展,其特点是方法和技术的发展,使机器能够感知环境、从数据中学习并做出决策。本摘要主要关注从与 AI 相关的各种研究成果中获得的见解。 +> +> #### 主要发现 +> +> 1. **人工智能的定义和范围**: +> +> - 人工智能被定义为计算机科学的一个分支,专注于创建能够执行需要类似人类智能的任务的系统,包括学习、推理和解决问题(维基百科)。 +>——它涵盖了各种子领域,包括机器学习、自然语言处理、机器人和计算机视觉。 +> +> 2. **影响与应用**: +> +> - AI 技术正在融入众多领域,提高效率和生产力。应用范围从自动驾驶汽车和医疗诊断到客户服务自动化和财务预测(OpenAI)。 +> - 谷歌致力于让人工智能造福每个人,这凸显了其通过增强各个平台的用户体验(谷歌人工智能)显著改善日常生活的潜力。 +> +> 3. **道德考虑**: +> +> - 关于人工智能的伦理影响的讨论一直在进行中,包括对隐私、偏见和决策过程中的责任的担忧。强调需要一个确保安全和负责任地使用人工智能技术的框架(OpenAI)。 +> +> 4. **学习机制**: +> +> - AI 系统利用不同的学习机制,例如监督学习、无监督学习和强化学习。这些方法允许 AI 通过从过去的经验和数据中学习来提高性能(维基百科)。 +> - 监督学习和无监督学习之间的区别至关重要;监督学习依赖于标记数据,而无监督学习则识别没有预定义标签的模式(无监督)。 +> +> 5. **未来方向**: +> - 未来人工智能的发展预计将专注于增强人工智能系统的可解释性和透明度,确保它们能够提供合理的决策和行动(OpenAI)。 +> - 人们还在努力使人工智能系统更易于访问和用户友好,鼓励不同人群和行业更广泛地采用它(谷歌人工智能)。 +> +> #### 结论 +> +> 人工智能代表着跨多个领域的变革力量,有望重塑行业并改善生活质量。然而,随着其能力的扩展,解决随之而来的伦理和社会影响至关重要。技术专家、伦理学家和政策制定者之间的持续研究和合作对于驾驭人工智能的未来格局至关重要。
- -## 简介 +## 安装 -Julep 是一个开源平台,用于创建具有可定制工作流的持久 AI 代理。它提供了开发、管理和部署 AI 驱动应用程序的工具,注重灵活性和易用性。 +要开始使用 Julep,请使用 [npm](https://www.npmjs.com/package/@julep/sdk) 或 [pip](https://pypi.org/project/julep/) 安装它: -使用 Julep,您可以: -- 快速开发能够在多次交互中保持上下文和状态的 AI 代理 -- 设计和执行针对您的 AI 代理定制的复杂工作流 -- 无缝集成各种工具和 API 到您的 AI 工作流中 -- 轻松管理持久会话和用户交互 +**Node.js**: -无论您是在开发聊天机器人、自动化任务,还是构建复杂的 AI 助手,Julep 都能为您提供所需的灵活性和功能,帮助您快速高效地将想法转化为现实。 +```bash +npm install @julep/sdk - +# or -
-这里有一个简单的 Python 示例: +bun add @julep/sdk +``` - +**Python**: -

-from julep import Julep, AsyncJulep
+```bash
+pip install julep
+```
+
+> [!注意]
+> 从[此处](https://dashboard-dev.julep.ai)获取您的 API 密钥。
+>
+> 虽然我们处于测试阶段,但您也可以通过 [Discord](https://discord.com/invite/JTSBGRZrzj) 联系,以解除 API 密钥的速率限制。
+
+> [!提示]
+> 💻 你是“向我展示代码!”的那种人吗?我们创建了大量的烹饪书供您入门。**查看 [烹饪书](https://github.com/julep-ai/julep/tree/dev/cookbooks)** 以浏览示例。
+>
+> 💡 您还可以在 Julep 的基础上构建许多想法。**查看[想法列表](https://github.com/julep-ai/julep/tree/dev/cookbooks/IDEAS.md)** 以获取一些灵感。
 
-# 🔑 初始化 Julep 客户端
-#     或者使用 AsyncJulep 进行异步操作
-client = Julep(api_key="your_api_key")
+## Python 快速入门🐍
 
-##################
-## 🤖 代理 🤖 ##
-##################
+````python
+### Step 0: Setup
+
+import time
+import yaml
+from julep import Julep # or AsyncJulep
+
+client = Julep(api_key="your_julep_api_key")
+
+### Step 1: Create an Agent
 
-# 创建一个研究代理
 agent = client.agents.create(
-    name="研究代理",
+    name="Storytelling Agent",
     model="claude-3.5-sonnet",
-    about="您是一个设计用于处理研究查询的研究代理。",
+    about="You are a creative storyteller that crafts engaging stories on a myriad of topics.",
 )
 
-# 🔍 为代理添加工具
-client.agents.tools.create(
-    agent_id=agent.id,
-    name="web_search",  # 应该是有效的 Python 变量名
-    description="使用此工具进行研究查询。",
-    integration={
-        "provider": "brave",
-        "method": "search",
-        "setup": {
-            "api_key": "your_brave_api_key",
-        },
-    },
+### Step 2: Create a Task that generates a story and comic strip
+
+task_yaml = """
+name: Storyteller
+description: Create a story based on an idea.
+
+tools:
+  - name: research_wikipedia
+    type: integration
+    integration:
+      provider: wikipedia
+      method: search
+
+main:
+  # Step 1: Generate plot idea
+  - prompt:
+      - role: system
+        content: You are {{agent.name}}. {{agent.about}}
+      - role: user
+        content: >
+          Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside ```yaml 标签位于您的回复末尾。
+展开:true
+
+- 评价:
+情节想法:load_yaml(_.split('```yaml')[1].split('```')[0].strip())
+
+# 第二步:从情节思路中提取研究领域
+- 迅速的:
+- 角色:系统
+内容:您是 {{agent.name}}。{{agent.about}}
+- 角色:用户
+内容: >
+以下是一些故事情节的想法:
+{% 表示 _.plot_ideas 中的想法 %}
+- {{主意}}
+{% 结束 %}
+
+为了发展故事情节,我们需要研究情节思路。
+我们应该研究什么?写下你认为有趣的情节想法的维基百科搜索查询。
+将输出作为 yaml 列表返回```yaml tags at the end of your response.
+    unwrap: true
+    settings:
+      model: gpt-4o-mini
+      temperature: 0.7
+
+  - evaluate:
+      research_queries: load_yaml(_.split('```yaml')[1].split('```')[0].strip())
+
+  # Step 3: Research each plot idea
+  - foreach:
+      in: _.research_queries
+      do:
+        tool: research_wikipedia
+        arguments:
+          query: _
+
+  - evaluate:
+      wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])'
+
+  # Step 4: Think and deliberate
+  - prompt:
+      - role: system
+        content: You are {{agent.name}}. {{agent.about}}
+      - role: user
+        content: |-
+          Before we write the story, let's think and deliberate. Here are some plot ideas:
+          {% for idea in outputs[1].plot_ideas %}
+          - {{idea}}
+          {% endfor %}
+
+          Here are the results from researching the plot ideas on Wikipedia:
+          {{_.wikipedia_results}}
+
+          Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story.
+          Write down all your notes and thoughts.
+          Then finally write the plot as a yaml object inside ```yaml 标签位于响应末尾。yaml 对象应具有以下结构:
+
+          ```yaml
+          title: ""
+          characters:
+          - name: ""
+            about: ""
+          synopsis: ""
+          scenes:
+          - title: ""
+            description: ""
+            characters:
+            - name: ""
+              role: ""
+            plotlines:
+            - ""```
+
+确保 yaml 有效,且角色和场景不为空。还要注意分号和编写 yaml 的其他问题。
+展开:true
+
+- 评价:
+情节:“load_yaml(_.split('```yaml')[1].split('```')[0].strip())”
+"""
+
+任务 = 客户端.任务.创建(
+agent_id=代理.id,
+**yaml.safe_load(任务_yaml)
 )
 
-#################
-## 💬 聊天 💬 ##
-#################
+### 步骤 3:执行任务
 
-# 与代理开始交互式聊天会话
-session = client.sessions.create(
-    agent_id=agent.id,
-    context_overflow="adaptive",  # 🧠 Julep 将在需要时动态计算上下文窗口
+执行 = 客户端.执行.创建(
+任务ID=任务ID,
+输入={“idea”:“一只学飞的猫”}
 )
 
-# 🔄 聊天循环
-while (user_input := input("您:")) != "退出":
-    response = client.sessions.chat(
-        session_id=session.id,
-        message=user_input,
-    )
-
-    print("代理:", response.choices[0].message.content)
-
-
-#################
-## 📋 任务 📋 ##
-#################
-
-# 为代理创建一个周期性研究任务
-task = client.tasks.create(
-    agent_id=agent.id,
-    name="研究任务",
-    description="每24小时研究给定的主题。",
-    #
-    # 🛠️ 任务特定工具
-    tools=[
-        {
-            "name": "send_email",
-            "description": "向用户发送包含结果的电子邮件。",
-            "api_call": {
-                "method": "post",
-                "url": "https://api.sendgrid.com/v3/mail/send",
-                "headers": {"Authorization": "Bearer YOUR_SENDGRID_API_KEY"},
-            },
-        }
-    ],
-    #
-    # 🔢 任务主要步骤
-    main=[
-        #
-        # 步骤 1:研究主题
-        {
-            # `_`(下划线)变量指向上一步的输出
-            # 这里,它指向用户输入的主题
-            "prompt": "查找主题 '{{_.topic}}' 并总结结果。",
-            "tools": [{"ref": {"name": "web_search"}}],  # 🔍 使用代理的网络搜索工具
-            "unwrap": True,
-        },
-        #
-        # 步骤 2:发送包含研究结果的电子邮件
-        {
-            "tool": "send_email",
-            "arguments": {
-                "subject": "研究结果",
-                "body": "'以下是今天的研究结果:' + _.content",
-                "to": "inputs[0].email",  # 引用用户输入的电子邮件
-            },
-        },
-        #
-        # 步骤 3:等待 24 小时后重复
-        {"sleep": "24 * 60 * 60"},
-    ],
-)
+# 🎉 观看故事和漫画面板的生成
+当(result:= client.executions.get(execution.id)).status 不在['succeeded','failed']中时:
+打印(结果.状态,结果.输出)
+时间.睡眠(1)
 
-# 🚀 启动周期性任务
-client.executions.create(task_id=task.id, input={"topic": "Python"})
+# 📦执行完成后,检索结果
+如果 result.status ==“成功”:
+打印(结果.输出)
+别的:
+引发异常(结果.错误)
+````
 
-# 🔁 这将每 24 小时运行一次任务,
-#    研究 "Python" 主题,并
-#    将结果发送到用户的电子邮件
-
-
+You can find the full python example [here](example.py). -## 特性 +
+ + Back to Top +  |  + + Table of Contents + +
-Julep 简化了构建具有可定制工作流的持久 AI 代理的过程。主要特性包括: +## Node.js Quick Start 🟩 -- **持久 AI 代理**:创建和管理能够在多次交互中保持上下文的 AI 代理。 -- **可定制工作流**:使用任务(Tasks)设计复杂的多步骤 AI 工作流。 -- **工具集成**:无缝集成各种工具和 API 到您的 AI 工作流中。 -- **文档管理**:高效管理和搜索代理的文档。 -- **会话管理**:处理持久会话以实现连续交互。 -- **灵活执行**:支持工作流中的并行处理、条件逻辑和错误处理。 +```JavaScript的 +// 步骤 0:设置 +const dotenv = require(“dotenv”); +const { Julep } = require(“@julep/sdk”); +const yaml = require(“yaml”); -## 安装 +dotenv.配置(); -要开始使用 Julep,请使用 [npm](https://www.npmjs.com/package/@julep/sdk) 或 [pip](https://pypi.org/project/julep/) 安装: +const 客户端 = new Julep({ +apiKey:process.env.JULEP_API_KEY, +环境:process.env.JULEP_ENVIRONMENT || “生产”, +}); -```bash -npm install @julep/sdk +/* 步骤 1:创建代理 */ + +异步函数 createAgent() { +const 代理 = 等待客户端.代理.创建({ +名称:“讲故事特工”, +模型:“claude-3.5-sonnet”, +关于: +“您是一位富有创意的讲故事者,能就无数主题创作出引人入胜的故事。” + }); +回報代理; +} + +/* 步骤 2:创建一个生成故事和漫画的任务 */ + +const taskYaml = ` +名称:讲故事的人 +描述:根据一个想法创建一个故事。 + +工具: +- 名称:research_wikipedia +一体化: +提供者:维基百科 +方法:搜索 + +主要的: +# 步骤 1:生成情节想法 +- 迅速的: +- 角色:系统 +内容:您是 {{agent.name}}。{{agent.about}} +- 角色:用户 +内容: > +根据想法“{{_.idea}}”,生成 5 个情节想法的列表。尽情发挥你的想象力和创造力。将输出作为响应末尾的 \`\`\`yaml 标签内的长字符串列表返回。 +展开:true + +- 评价: +plot_ideas:load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# 第二步:从情节思路中提取研究领域 +- 迅速的: +- 角色:系统 +内容:您是 {{agent.name}}。{{agent.about}} +- 角色:用户 +内容: > +以下是一些故事情节的想法: +{% 表示 _.plot_ideas 中的想法 %} +- {{主意}} +{% 结束 %} + +为了发展故事情节,我们需要研究情节思路。 +我们应该研究什么?写下你认为有趣的情节想法的维基百科搜索查询。 +将您的输出作为 yaml 列表返回到响应末尾的 \`\`\`yaml 标签内。 +展开:true +设置: +型号:gpt-4o-mini +温度:0.7 + +- 评价: +research_queries:load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# 步骤 3:研究每个情节构思 +- foreach: +在:_.research_queries +做: +工具:research_wikipedia +参数: +询问: _ + +- 评价: +wikipedia_results:'NEWLINE.join([f“- {doc.metadata.title}:{doc.metadata.summary}”用于 item in _ for doc in item.documents])' + +# 第 4 步:思考和深思 +- 迅速的: +- 角色:系统 +内容:您是 {{agent.name}}。{{agent.about}} +- 角色:用户 +内容:|- +在写故事之前,让我们先思考一下。以下是一些情节构思: +{% for idea in output[1].plot_ideas %} +- {{主意}} +{% 结束 %} + +以下是在维基百科上研究情节思路的结果: +{{_.wikipedia_results}} + +认真思考故事情节。将故事情节与维基百科搜索结果相结合,为故事创建详细情节。 +写下你所有的笔记和想法。 +最后,将图表作为 yaml 对象写入响应末尾的 \`\`\`yaml 标签内。yaml 对象应具有以下结构: + +\`\`\`yaml +标题: ”" +人物: +- 姓名: ”" +关于: ”" +概要:”" +场景: +- 标题: ”" +描述: ”" +人物: +- 姓名: ”" +角色: ”" +故事情节: +-”“\`\`\` + +确保 yaml 有效,且角色和场景不为空。还要注意分号和编写 yaml 的其他问题。 +展开:true + +- 评价: +情节:“load_yaml(_。split('\`\`\`yaml')[1].split('\`\`\`')[0].strip())” +`; + +异步函数 createTask(agentId){ +const task = 等待客户端.tasks.创建(agentId, yaml.parse(taskYaml)); +返回任务; +} + +/* 步骤 3:执行任务 */ + +异步函数 executeTask (taskId) { +const 执行 = 等待客户端.执行.创建(taskId,{ +输入:{想法:“一只学飞的猫”}, + }); + +// 🎉 观看故事和漫画面板的生成 +while (真) { +const result = 等待客户端.executions.get(execution.id); +控制台.log(结果.状态,结果.输出); + +if (result.status === "成功" || result.status === "失败") { +// 📦执行完成后,检索结果 +如果 (result.status === "成功") { +控制台.log(结果.输出); +} 别的 { +抛出新的错误(result.error); + } +休息; + } + +等待新的Promise((resolve)=> setTimeout(resolve,1000)); + } +} + +// 运行示例的主函数 +异步函数 main() { +尝试 { +const agent = await createAgent(); +const task = await createTask(agent.id); +等待执行任务(任务id); +} 捕获 (错误) { +console.error("发生错误:", error); + } +} + +主要的() +.then(() => console.log("完成")) +.catch(控制台.错误); ``` -或 +You can find the full Node.js example [here](example.js). -```bash -pip install julep +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Components + +Julep is made up of the following components: + +- **Julep Platform**: The Julep platform is a cloud service that runs your workflows. It includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. +- **Julep SDKs**: Julep SDKs are a set of libraries for building workflows. There are SDKs for Python and JavaScript, with more on the way. +- **Julep API**: The Julep API is a RESTful API that you can use to interact with the Julep platform. + +### Mental Model + +
+ +
+ +Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's how to visualize it: + +1. **Your Application Code:** + + - You can use the Julep SDK in your application to define agents, tasks, and workflows. + - The SDK provides functions and classes that make it easy to set up and manage these components. + +2. **Julep Backend Service:** + + - The SDK communicates with the Julep backend over the network. + - The backend handles execution of tasks, maintains session state, stores documents, and orchestrates workflows. + +3. **Integration with Tools and APIs:** + - Within your workflows, you can integrate external tools and services. + - The backend facilitates these integrations, so your agents can, for example, perform web searches, access databases, or call third-party APIs. + +## Concepts + +Julep is built on several key technical components that work together to create powerful AI workflows: + +```美人鱼 +图 TD +用户[用户] ==> 会话[会话] +会话-->代理[代理] +代理-->任务[任务] +代理——> LLM[大型语言模型] +任务 --> 工具[工具] +代理人 --> 文件[文件] +文档 --> VectorDB[矢量数据库] +任务 --> 执行[执行] + +classDef 客户端填充:#9ff,描边:#333,描边宽度:1px; +用户客户端类; + +classDef 核心填充:#f9f,描边:#333,描边宽度:2px; +类代理、任务、会话核心; +``` + +- **Agents**: AI-powered entities backed by large language models (LLMs) that execute tasks and interact with users. +- **Users**: Entities that interact with agents through sessions. +- **Sessions**: Stateful interactions between agents and users, maintaining context across multiple exchanges. +- **Tasks**: Multi-step, programmatic workflows that agents can execute, including various types of steps like prompts, tool calls, and conditional logic. +- **Tools**: Integrations that extend an agent's capabilities, including user-defined functions, system tools, or third-party API integrations. +- **Documents**: Text or data objects associated with agents or users, vectorized and stored for semantic search and retrieval. +- **Executions**: Instances of tasks that have been initiated with specific inputs, with their own lifecycle and state machine. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Understanding Tasks + +Tasks are the core of Julep's workflow system. They allow you to define complex, multi-step AI workflows that your agents can execute. Here's a brief overview of task components: + +- **Name, Description and Input Schema**: Each task has a unique name and description for easy identification. An input schema (optional) that is used to validate the input to the task. +- **Main Steps**: The core of a task, defining the sequence of actions to be performed. Each step can be a prompt, tool call, evaluate, wait_for_input, log, get, set, foreach, map_reduce, if-else, switch, sleep, or return. (See [Types of Workflow Steps](#types-of-workflow-steps) for more details) +- **Tools**: Optional integrations that extend the capabilities of your agent during task execution. + +### Lifecycle of a Task + +You create a task using the Julep SDK and specify the main steps that the agent will execute. When you execute a task, the following lifecycle happens: + +```美人鱼 +顺序图 +参与者 D 作为您的代码 +参与者 C 作为 Julep 客户 +参与者 S 担任 Julep 服务器 + +D->>C:创建任务 +C->>S:提交执行 +注意 S:执行任务 +S 注释:管理状态 +S-->>C:执行事件 +C-->>D:进度更新 +S->>C:执行完成 +C->>D:最终结果 +``` + +### Types of Workflow Steps + +Tasks in Julep can include various types of steps, allowing you to create complex and powerful workflows. Here's an overview of the available step types: + +#### Common Steps + + + + + + + + + + + + + + + + -首先,将 Julep SDK 导入到您的项目中: + + + + + + + + + + + -const agent = await julep.agents.create({ - name: '研究助手', - model: 'gpt-4-turbo', - about: "您是一个创意讲故事代理,能够根据想法创作引人入胜的故事并生成漫画面板。", -}); + + + + + + + + +
NameAboutSyntax
Prompt +Send a message to the AI model and receive a response +

Note: The prompt step uses Jinja templates and you can access context variables in them. +
+ +```yaml +- prompt: "分析以下数据:{{agent.name}}" # <-- 这是一个 jinja 模板 ``` -> [!TIP] -> 在测试阶段,您可以通过 [Discord](https://discord.com/invite/JTSBGRZrzj) 获取 API 密钥。 +```yaml +- 迅速的: +- 角色:系统 +内容:“您是 {{agent.name}}。 {{agent.about}}” +- 角色:用户 +内容:“分析以下数据:{{_.data}}” +``` -## 快速入门指南 +
Tool Call +Execute an integrated tool or API that you have previously declared in the task. +

Note: The tool call step uses Python expressions inside the arguments. -### 步骤 1:导入 Julep +
-```javascript -const Julep = require('@julep/sdk'); +```yaml +- 工具:web_search +参数: +查询:“最新的 AI 发展”#<- 这是一个 Python 表达式(注意引号) +num_results: len(_.topics) # <-- 用于访问列表长度的 Python 表达式 ``` -或 +
Evaluate +Perform calculations or manipulate data +

Note: The evaluate step uses Python expressions. +
-```python -from julep import AsyncJulep +```yaml +- 评价: +平均分数:总分(分数)/长度(分数) ``` -### 步骤 2:初始化代理 +
Wait for Input +Pause workflow until input is received. It accepts an `info` field that can be used by your application to collect input from the user. -使用基本设置创建一个新代理: +

Note: The wait_for_input step is useful when you want to pause the workflow and wait for user input e.g. to collect a response to a prompt. -```javascript -const julep = new Julep({ apiKey: 'your-api-key' }); +
+ +```yaml +-等待输入: +信息: +消息:'“请提供有关 {_.required_info} 的其他信息。”' # <-- 用于访问上下文变量的 python 表达式 ``` -或 +
Log +Log a specified value or message. -```python -client = AsyncJulep(api_key="your_api_key") +

Note: The log step uses Jinja templates and you can access context variables in them. -agent = await client.agents.create( - name="讲故事代理", - model="gpt-4-turbo", - about="您是一个创意讲故事代理,能够根据想法创作引人入胜的故事并生成漫画面板。", -) +
+ +```yaml +- log:“项目 {{_.item_id}} 的处理已完成”#<-- jinja 模板用于访问上下文变量 ``` -### 步骤 3:与代理聊天 +
-与代理开始交互式聊天会话: +#### Key-Value Steps -```javascript -const session = await julep.sessions.create({ - agentId: agent.id, -}); + + + + + + + -console.log(response); + + + + + -print(response) + + +
Name About Syntax
Get +Retrieve a value from the execution's key-value store. -// 向代理发送消息 -const response = await julep.sessions.chat({ - sessionId: session.id, - message: '你好,能给我讲个故事吗?', -}); + + +```yaml +- 获取:用户偏好 ``` -或 +
Set +Assign a value to a key in the execution's key-value store. -```python -session = await client.sessions.create(agent_id=agent.id) +

Note: The set step uses Python expressions. -# 向代理发送消息 -response = await client.sessions.chat( - session_id=session.id, - message="你好,能给我讲个故事吗?", -) +
+ +```yaml +- 放: +user_preference: '"dark_mode"' # <-- python 表达式 ``` -### 步骤 4:创建多步骤任务 +
-让我们定义一个多步骤任务,根据输入的想法创建故事并生成分镜漫画: +#### Iteration Steps -```python -# 🛠️ 为代理添加图像生成工具(DALL·E) -await client.agents.tools.create( - agent_id=agent.id, - name="image_generator", - description="使用此工具根据描述生成图像。", - integration={ - "provider": "dalle", - "method": "generate_image", - "setup": { - "api_key": "your_dalle_api_key", - }, - }, -) + + + + + + + + + + + + + -```python -# 🚀 执行任务,输入一个想法 -execution = await client.executions.create( - task_id=task.id, - input={"idea": "一只学会飞翔的猫"} -) + + + + + + + + +
Name About Syntax
Foreach +Iterate over a collection and perform steps for each item -# 📋 任务 -# 创建一个任务,接受一个想法并创建故事和 4 格漫画 -task = await client.tasks.create( - agent_id=agent.id, - name="故事和漫画创作器", - description="根据一个想法创作故事并生成 4 格漫画来说明故事。", - main=[ - # 步骤 1:生成故事并将其概括为 4 个面板 - { - "prompt": [ - { - "role": "system", - "content": "您是 {{agent.name}}。{{agent.about}}" - }, - { - "role": "user", - "content": ( - "基于想法 '{{_.idea}}',写一个适合 4 格漫画的短故事。" - "提供故事和一个编号列表,包含 4 个简短描述,每个描述对应一个面板,说明故事中的关键时刻。" - ), - }, - ], - "unwrap": True, - }, - # 步骤 2:提取面板描述和故事 - { - "evaluate": { - "story": "_.split('1. ')[0].strip()", - "panels": "re.findall(r'\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)', _)", - } - }, - # 步骤 3:使用图像生成器工具为每个面板生成图像 - { - "foreach": { - "in": "_.panels", - "do": { - "tool": "image_generator", - "arguments": { - "description": "_", - }, - }, - }, - }, - # 步骤 4:为故事生成一个吸引人的标题 - { - "prompt": [ - { - "role": "system", - "content": "您是 {{agent.name}}。{{agent.about}}" - }, - { - "role": "user", - "content": "根据以下故事,生成一个吸引人的标题。\n\n故事:{{outputs[1].story}}", - }, - ], - "unwrap": True, - }, - # 步骤 5:返回故事、生成的图像和标题 - { - "return": { - "title": "outputs[3]", - "story": "outputs[1].story", - "comic_panels": "[output.image.url for output in outputs[2]]", - } - }, - ], -) + + +```yaml +- foreach: +in: _.data_list # <-- 用于访问上下文变量的 python 表达式 +做: +- log: "处理项目 {{_.item}}" # <-- jinja 模板访问上下文变量 ``` -> [!TIP] -> Node.js 版本的代码类似。 +
Map-Reduce +Map over a collection and reduce the results -### 步骤 5:执行任务 + -# 🎉 观看故事和漫画面板的生成过程 -await client.executions.stream(execution_id=execution.id) +```yaml +- 映射_减少: +over: _.numbers # <-- 用于访问上下文变量的 python 表达式 +地图: +- 评价: +平方:“_ ** 2” +reduce:results + [_] # <--(可选)python 表达式以减少结果。如果省略,则为默认值。 ``` -这个例子展示了如何创建一个带有自定义工具的代理,定义一个复杂的多步骤任务,并执行它以生成创意输出。 +```yaml +- 映射_减少: +结束:_.topics +地图: +- 提示:写一篇关于{{__}}的文章 +并行度:10 +``` - +
Parallel +Run multiple steps in parallel + + + +```yaml +- 平行线: +- 工具:web_search +参数: +查询:“AI 新闻” +- 工具:weather_check +参数: +地点:“纽约” +``` -> [!TIP] -> 您可以在[这里](example.ts)找到另一个 Node.js 示例,或在[这里](example.py)找到 Python 示例。 +
-## 概念 +#### Conditional Steps -Julep 建立在几个关键的技术组件之上,这些组件协同工作以创建强大的 AI 工作流: + + + + + + + + + + + + + + + + +
Name About Syntax
If-Else +Conditional execution of steps -### 代理 -由大型语言模型(LLM)支持的 AI 实体,执行任务并与用户交互。代理是 Julep 的核心功能单元。 + + +```yaml +- if: _.score > 0.8 # <-- python 表达式 +然后: +- 日志:取得高分 +别的: +- 错误:分数需要提高 +``` -```mermaid -graph TD - Agent[代理] --> LLM[大型语言模型] - Agent --> Tasks[任务] - Agent --> Users[用户] - Tasks --> Tools[工具] +
Switch +Execute steps based on multiple conditions + + + +```yaml +- 转变: +- 案例:_.category =='A' +然后: +- 日志:“A 类处理” +- 案例:_.category =='B' +然后: +- 日志:“B 类处理” +- case: _ # 默认情况 +然后: +- 错误:未知类别 ``` -### 用户 -与代理交互的实体。用户可以与会话关联,并拥有自己的元数据,允许个性化交互。 +
-```mermaid -graph LR - User[用户] --> Sessions[会话] - Sessions --> Agents[代理] - Sessions --> Metadata[元数据] +#### Other Control Flow + + + + + + + + + + + + + + + + + + + + -```mermaid -graph TD - Tasks[任务] --> Steps[工作流步骤] - Steps --> Prompt[提示] - Steps --> ToolCalls[工具调用] - Steps --> ConditionalLogic[条件逻辑] + + + + + + -```mermaid -graph LR - Tools[工具] --> UserDefinedFunctions[用户定义函数] - Tools --> SystemTools[系统工具] - Tools --> ThirdPartyAPIs[第三方 API] + + +
Name About Syntax
Sleep +Pause the workflow for a specified duration + + + +```yaml +- 睡觉: +秒:30 +# 分钟:1 +#小时数:1 +#天数:1 ``` -### 会话 -代理和用户之间的有状态交互。会话在多次交换中保持上下文,可以配置不同的行为,包括上下文管理和溢出处理。 +
Return +Return a value from the workflow -```mermaid -graph LR - Sessions[会话] --> Agents[代理] - Sessions --> Users[用户] - Sessions --> ContextManagement[上下文管理] - Sessions --> OverflowHandling[溢出处理] +

Note: The return step uses Python expressions. + +
+ +```yaml +- 返回: +result: '“任务成功完成”' #<-- python 表达式 +时间:datetime.now().isoformat() # <-- python 表达式 ``` -### 任务 -代理可以执行的多步骤、程序化工作流。任务定义复杂操作,可以包括各种类型的步骤,如提示、工具调用和条件逻辑。 +
Yield +Run a subworkflow and await its completion + + + +```yaml +- 屈服: +工作流程:process_data +参数: +输入数据:_.raw_data # <-- python 表达式 ``` -### 工具 -扩展代理能力的集成。工具可以是用户定义的函数、系统工具或第三方 API 集成。它们允许代理执行超出文本生成的操作。 +
Error +Handle errors by specifying an error message + + + +```yaml +- 错误:“提供的输入无效”#<-- 仅限字符串 ``` -### 文档 -可以与代理或用户关联的文本或数据对象。文档被向量化并存储在向量数据库中,在代理交互期间实现语义搜索和检索。 +
+ +Each step type serves a specific purpose in building sophisticated AI workflows. This categorization helps in understanding the various control flows and operations available in Julep tasks. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Tool Types + +Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a `web_search(query)` tool to search the Internet for some information. + +Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. + +Tools in julep can be one of: + +1. **User-defined `functions`**: These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. They need to be handled by the client. The workflow will pause until the client calls the function and gives the results back to julep. +2. **`system` tools**: Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. +3. **`integrations`**: Built-in third party tools that can be used to extend the capabilities of your agents. +4. **`api_calls`**: Direct api calls during workflow executions as tool calls. + +### User-defined `functions` + +These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + +```yaml +名称:示例系统工具任务 +描述:使用系统调用列出代理 -```mermaid -graph LR - Documents[文档] --> VectorDatabase[向量数据库] - Documents --> SemanticSearch[语义搜索] - Documents --> AgentsOrUsers[代理或用户] +工具: +- 名称:send_notification +描述:向用户发送通知 +类型:函数 +功能: +参数: +类型:对象 +特性: +文本: +类型:字符串 +描述:通知内容 + +主要的: +- 工具:send_notification +参数: +内容:'“hi”'#<--python 表达式 ``` -### 执行 -已经用特定输入启动的任务实例。执行有自己的生命周期和状态机,允许监控、管理和恢复长时间运行的进程。 +Whenever julep encounters a _user-defined function_, it pauses, giving control back to the client and waits for the client to run the function call and give the results back to julep. + +### `system` tools + +Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + +`system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + +For example, + +```yaml +名称:示例系统工具任务 +描述:使用系统调用列出代理 + +工具: +- 名称:list_agent_docs +描述:列出给定代理的所有文档 +类型:系统 +系统: +资源:代理 +子资源:doc +操作:列表 -```mermaid -graph LR - Executions[执行] --> Tasks[任务] - Executions --> Lifecycle[生命周期] - Executions --> Monitoring[监控] - Executions --> Management[管理] - Executions --> Resumption[恢复] +主要的: +- 工具:list_agents +参数: +限制:10 #<-- python 表达式 ``` -有关这些概念及其交互的更详细解释,请参阅我们的[概念文档](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)。 +#### Available `system` resources and operations -## 理解任务 +- `agent`: -任务是 Julep 工作流系统的核心。它们允许您定义复杂的多步骤 AI 工作流,供您的代理执行。以下是任务组件的简要概述: + - `list`: List all agents. + - `get`: Get a single agent by id. + - `create`: Create a new agent. + - `update`: Update an existing agent. + - `delete`: Delete an existing agent. -- **名称和描述**:每个任务都有唯一的名称和描述,便于识别。 -- **主要步骤**:任务的核心,定义了要执行的操作序列。 -- **工具**:可选的集成,在任务执行期间扩展代理的能力。 +- `user`: -### 工作流步骤类型 + - `list`: List all users. + - `get`: Get a single user by id. + - `create`: Create a new user. + - `update`: Update an existing user. + - `delete`: Delete an existing user. -Julep 中的任务可以包含各种类型的步骤: +- `session`: -1. **提示**:向 AI 模型发送消息并接收响应。 - ```python - {"prompt": "分析以下数据:{{data}}"} - ``` + - `list`: List all sessions. + - `get`: Get a single session by id. + - `create`: Create a new session. + - `update`: Update an existing session. + - `delete`: Delete an existing session. + - `chat`: Chat with a session. + - `history`: Get the chat history with a session. -2. **工具调用**:执行集成的工具或 API。 - ```python - {"tool": "web_search", "arguments": {"query": "最新 AI 发展"}} - ``` +- `task`: -3. **评估**:执行计算或操作数据。 - ```python - {"evaluate": {"average_score": "sum(scores) / len(scores)"}} - ``` - -4. **条件逻辑**:基于条件执行步骤。 - ```python - {"if": "score > 0.8", "then": [...], "else": [...]} - ``` + - `list`: List all tasks. + - `get`: Get a single task by id. + - `create`: Create a new task. + - `update`: Update an existing task. + - `delete`: Delete an existing task. -5. **循环**:遍历数据或重复步骤。 - ```python - {"foreach": {"in": "data_list", "do": [...]}} - ``` +- `doc` (subresource for `agent` and `user`): + - `list`: List all documents. + - `create`: Create a new document. + - `delete`: Delete an existing document. + - `search`: Search for documents. -| 步骤类型 | 描述 | 输入 | -|---------|------|------| -| **提示** | 向 AI 模型发送消息并接收响应。 | 提示文本或模板 | -| **工具调用** | 执行集成的工具或 API。 | 工具名称和参数 | -| **评估** | 执行计算或操作数据。 | 要评估的表达式或变量 | -| **等待输入** | 暂停工作流直到收到输入。 | 任何所需的用户或系统输入 | -| **日志** | 记录指定的值或消息。 | 要记录的消息或值 | -| **嵌入** | 将文本嵌入到特定格式或系统中。 | 要嵌入的文本或内容 | -| **搜索** | 基于查询执行文档搜索。 | 搜索查询 | -| **获取** | 从键值存储中检索值。 | 键标识符 | -| **设置** | 在键值存储中为键分配值。 | 要分配的键和值 | -| **并行** | 并行运行多个步骤。 | 要同时执行的步骤列表 | -| **遍历** | 遍历集合并为每个项目执行步骤。 | 要遍历的集合或列表 | -| **映射归约** | 对集合进行映射并基于表达式归约结果。 | 要映射和归约的集合和表达式 | -| **如果-否则** | 基于条件执行步骤。 | 要评估的条件 | -| **开关** | 基于多个条件执行步骤,类似于 switch-case 语句。 | 多个条件和相应的步骤 | -| **生成** | 运行子工作流并等待其完成。 | 子工作流标识符和输入数据 | -| **错误** | 通过指定错误消息来处理错误。 | 错误消息或处理指令 | -| **睡眠** | 暂停工作流指定的持续时间。 | 持续时间(秒、分钟等) | -| **返回** | 从工作流返回值。 | 要返回的值 | - -有关每种步骤类型的详细信息和高级用法,请参阅我们的[任务文档](https://docs.julep.ai/tasks)。 - -## 高级功能 - -Julep 提供了一系列高级功能来增强您的 AI 工作流: +Additional operations available for some resources: -### 为代理添加工具 - -通过集成外部工具和 API 来扩展代理的能力: - -```python -client.agents.tools.create( - agent_id=agent.id, - name="web_search", - description="搜索网络以获取信息。", - integration={ - "provider": "google", - "method": "search", - "setup": {"api_key": "your_google_api_key"}, - }, +- `embed`: Embed a resource (specific resources not specified in the provided code). +- `change_status`: Change the status of a resource (specific resources not specified in the provided code). +- `chat`: Chat with a resource (specific resources not specified in the provided code). +- `history`: Get the chat history with a resource (specific resources not specified in the provided code). +- `create_or_update`: Create a new resource or update an existing one (specific resources not specified in the provided code). + +Note: The availability of these operations may vary depending on the specific resource and implementation details. + +> [!TIP] > **Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +### Built-in `integrations` + +Julep comes with a number of built-in integrations (as described in the section below). `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + +See [Integrations](#integrations) for details on the available integrations. + +> [!TIP] > **Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +### Direct `api_calls` + +julep can also directly make api calls during workflow executions as tool calls. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + +For example, + +```yaml +名称:示例 api_call 任务 +工具: +- 类型:api_call +名字:你好 +API调用: +方法:GET +网址:https://httpbin.org/get + +主要的: +- 工具:你好 +参数: +json: +测试:_.input#<--python 表达式 +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Integrations + +Julep supports various integrations that extend the capabilities of your AI agents. Here's a list of available integrations and their supported arguments: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Brave Search + +```yaml +设置: +api_key: string # Brave Search 的 API 密钥 + +参数: +query: string # 使用 Brave 搜索的搜索查询 + +输出: +result: string # Brave Search 的结果 +``` + + + +**Example cookbook**: [cookbooks/02-sarcastic-news-headline-generator.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/02-sarcastic-news-headline-generator.ipynb) + +
BrowserBase + +```yaml +设置: +api_key: string # BrowserBase 的 API 密钥 +project_id: string # BrowserBase 的项目 ID +session_id: string #(可选)BrowserBase 的会话 ID + +参数: +urls: list[string] # 使用 BrowserBase 加载的 URL + +输出: +documents: list # 从 URL 加载的文档 +``` + + + +**Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +
Email + +```yaml +设置: +host: string # 电子邮件服务器的主机 +port: integer # 电子邮件服务器的端口 +用户:string#电子邮件服务器的用户名 +password: string # 邮件服务器的密码 + +参数: +to: string # 要发送电子邮件到的电子邮件地址 +from: string # 发送电子邮件的电子邮件地址 +subject: string # 电子邮件的主题 +body: string # 电子邮件正文 + +输出: +success: boolean # 邮件是否发送成功 +``` + + + +**Example cookbook**: [cookbooks/00-Devfest-Email-Assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/00-Devfest-Email-Assistant.ipynb) + +
Spider + +```yaml +设置: +spider_api_key: string # Spider 的 API 密钥 + +参数: +url: string # 获取数据的 URL +mode: string # 爬虫的类型(默认值:“scrape”) +params: dict # (可选)Spider API 的参数 + +输出: +documents: list # 从蜘蛛返回的文档 +``` + + + +**Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +
Weather + +```yaml +设置: +openweathermap_api_key: string # OpenWeatherMap 的 API 密钥 + +参数: +location: string # 获取天气数据的位置 + +输出: +result: string # 指定位置的天气数据 +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
Wikipedia + +```yaml +参数: +query: string # 搜索查询字符串 +load_max_docs:整数 #(可选)要加载的最大文档数。默认值为 2。 + +输出: +documents: list # 从 Wikipedia 搜索返回的文档 +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
FFmpeg + +```yaml +参数: +cmd: string # 要执行的 FFmpeg 命令 +file: string # 要处理的 base64 编码文件 + +输出: +fileoutput: string # FFmpeg 命令以 base64 编码输出的文件 +result: boolean # FFmpeg命令是否执行成功 +mime_type: string # 输出文件的 MIME 类型 +``` + +
Llama Parse + +```yaml +设置: +llamaparse_api_key: string # Llama Parse 的 API 密钥 +params: dict #(可选)Llama Parse 集成的附加参数 + +参数: +文件:字符串 | 数组# 要解析的 base64 编码文件或要加载的 http/https URL 数组。 +filename:string #(可选)。文件的文件名。默认为随机 UUID。仅当文件是 base64 编码字符串时才使用。 +params: dict #(可选)Llama Parse 集成的附加参数。覆盖设置参数。 +base64:boolean # 输入文件是否为 base64 编码。默认为 false。 + +输出: +documents: list # 从文档中解析的数据 +``` + +
Cloudinary + +```yaml + +方法:media_upload | media_edit # 用于 Cloudinary 集成的方法 + +设置: +cloudinary_cloud_name: string # 您的 Cloudinary 云名称 +cloudinary_api_key: string # 您的 Cloudinary API 密钥 +cloudinary_api_secret: string # 您的 Cloudinary API 密钥 +params: dict # (可选)Cloudinary 集成的附加参数 + +参数: +file: string # 文件上传的 URL。仅适用于 media_upload 方法。 +upload_params:dict #(可选)上传的附加参数。仅适用于 media_upload 方法。 +public_id:字符串 #(可选)文件的公共 ID。对于 media_edit 方法,它是必需的。对于 media_upload 方法,它是可选的。默认为随机 UUID。 +transformation:list[dict] # 应用于文件的转换。仅适用于 media_edit 方法。 +return_base64: boolean # 是否以 base64 编码返回文件。默认为 false。 + +输出: +url:string # 上传文件的 URL。仅适用于 media_upload 方法。 +meta_data: dict # 来自上传响应的附加元数据。仅适用于 media_upload 方法。 +public_id: string # 上传文件的公共 ID。仅适用于 media_upload 方法。 +经过转换的 URL:字符串 #(可选)转换后的 URL。仅适用于 media_edit 方法。 +base64:字符串#(可选)如果 return_base64 为真,则为 base64 编码的文件。 +``` + + + +**Example cookbook**: [cookbooks/05-video-processing-with-natural-language.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/05-video-processing-with-natural-language.ipynb) + +
+ +For more details, refer to our [Integrations Documentation](#integrations). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Other Features + +Julep offers a range of advanced features to enhance your AI workflows: + +### Adding Tools to Agents + +Extend your agent's capabilities by integrating external tools and APIs: + +```Python +客户端.代理.工具.创建( +agent_id=代理.id, +名称="web_search", +description="在网络上搜索信息。", +积分={ +“提供者”:“勇敢”, +“方法”:“搜索”, +“设置”:{“api_key”:“你的brave_api_key”}, +}, ) ``` -### 管理会话和用户 +### Managing Sessions and Users -Julep 为持久交互提供了强大的会话管理: +Julep provides robust session management for persistent interactions: -```python -session = client.sessions.create( - agent_id=agent.id, - user_id="user123", - context_overflow="adaptive" +```Python +会话 = 客户端.会话.创建( +agent_id=代理.id, +用户 ID=用户 ID, +context_overflow="自适应" ) # 在同一会话中继续对话 -response = client.sessions.chat( - session_id=session.id, - message="继续我们之前的对话。" +响应 = 客户端.会话.聊天( +session_id=会话id, +消息=[ + { +“角色”:“用户”, +"content": "跟进之前的对话。" + } + ] ) ``` -### 文档集成和搜索 +### Document Integration and Search -轻松管理和搜索代理的文档: +Easily manage and search through documents for your agents: -```python +```Python # 上传文档 -document = client.documents.create( - file="path/to/document.pdf", - metadata={"category": "research_paper"} +文档 = 客户端.代理.docs.创建( +title="人工智能进步", +content="人工智能正在改变世界...", +元数据={“category”:“research_paper”} ) # 搜索文档 -results = client.documents.search( - query="AI 进展", - filter={"category": "research_paper"} +结果 = 客户端.代理.docs.搜索( +text="AI 进步", +metadata_filter={“category”:“research_paper”} ) ``` -有关更多高级功能和详细用法,请参阅我们的[高级功能文档](https://docs.julep.ai/advanced-features)。 +
+ + Back to Top +  |  + + Table of Contents + +
+ +## 参考 + +### SDK 参考 + +- **Node.js** [SDK 参考](https://github.com/julep-ai/node-sdk/blob/main/api.md) | [NPM 包](https://www.npmjs.com/package/@julep/sdk) +- **Python** [SDK 参考](https://github.com/julep-ai/python-sdk/blob/main/api.md) | [PyPI 包](https://pypi.org/project/julep/) + +### API 参考 -## SDK 参考 +浏览我们的 API 文档以了解有关代理、任务和执行的更多信息: -- [Node.js SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) -- [Python SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) +- [代理 API](https://dev.julep.ai/api/docs#tag/agents) +- [任务 API](https://dev.julep.ai/api/docs#tag/tasks) +- [执行 API](https://dev.julep.ai/api/docs#tag/executions) -## API 参考 +
+ + Back to Top +  |  + + Table of Contents + +
+ +## 本地快速启动 + +**要求**: -探索我们全面的 API 文档,了解更多关于代理、任务和执行的信息: +- 安装了最新的docker compose -- [代理 API](https://api.julep.ai/api/docs#tag/agents) -- [任务 API](https://api.julep.ai/api/docs#tag/tasks) -- [执行 API](https://api.julep.ai/api/docs#tag/executions) +**步骤**: + +1. `git 克隆 https://github.com/julep-ai/julep.git` +2. `cd julep` +3. `docker 卷创建 cozo_backup` +4. `docker 卷创建 cozo_data` +5. `cp .env.example .env # <-- 编辑此文件` +6. `docker compose --env-file .env --profile temporary-ui --profile single-tenant --profile self-hosted-db up --build` + +
+ + Back to Top +  |  + + Table of Contents + +
-## 示例和教程 +--- -发现示例项目和教程,帮助您入门并基于提供的示例进行构建: +## Julep 和 LangChain 等有什么区别? -- [示例项目](https://github.com/julep-ai/julep/tree/main/examples) -- [教程](https://docs.julep.ai/tutorials) +### 不同的用例 -## 贡献 +可以将 LangChain 和 Julep 视为 AI 开发堆栈中具有不同重点的工具。 -我们欢迎对项目的贡献!了解如何贡献以及我们的行为准则: +LangChain 非常适合创建提示序列和管理与 LLM 的交互。它拥有庞大的生态系统,包含大量预构建的集成,如果您想快速启动和运行某些功能,这会非常方便。LangChain 非常适合涉及线性提示链和 API 调用的简单用例。 -- [贡献指南](https://github.com/julep-ai/julep/blob/main/CONTRIBUTING.md) -- [行为准则](https://github.com/julep-ai/julep/blob/main/CODE_OF_CONDUCT.md) +另一方面,Julep 更侧重于构建持久的 AI 代理,这些代理可以在长期交互​​中保持上下文。当您需要涉及多步骤任务、条件逻辑以及在代理流程中直接与各种工具或 API 集成的复杂工作流时,它会大放异彩。它从头开始设计,以管理持久会话和复杂的工作流。 -## 支持和社区 +如果您想构建一个需要执行以下操作的复杂 AI 助手,请使用 Julep: -加入我们的社区,获取帮助、提问和分享您的想法: +- 跟踪几天或几周内的用户互动。 +- 执行计划任务,例如发送每日摘要或监控数据源。 +- 根据之前的互动或存储的数据做出决策。 +- 作为其工作流程的一部分与多个外部服务进行交互。 -- [Discord](https://discord.com/invite/JTSBGRZrzj) -- [GitHub 讨论](https://github.com/julep-ai/julep/discussions) -- [Twitter](https://twitter.com/julep_ai) +然后 Julep 提供支持所有这些的基础设施,而无需您从头开始构建。 -## 许可证 +### 不同的外形尺寸 -本项目采用 [Apache License 2.0](https://github.com/julep-ai/julep/blob/main/LICENSE) 许可。 +Julep 是一个**平台**,其中包括用于描述工作流的语言、用于运行这些工作流的服务器以及用于与平台交互的 SDK。要使用 Julep 构建某些东西,您需要在“YAML”中编写工作流描述,然后在云中运行工作流。 -## 致谢 +Julep 专为繁重、多步骤和长时间运行的工作流程而构建,并且工作流程的复杂程度没有限制。 -我们要感谢所有贡献者和开源社区为他们宝贵的资源和贡献。 +LangChain 是一个**库**,其中包含一些工具和一个用于构建线性提示和工具链的框架。为了使用 LangChain 构建某些东西,您通常需要编写 Python 代码来配置和运行要使用的模型链。 + +对于涉及线性提示和 API 调用链的简单用例,LangChain 可能足够并且能够更快地实现。 + +### 总之 + +当您需要在无状态或短期环境中管理 LLM 交互和提示序列时,请使用 LangChain。 + +当您需要一个具有高级工作流功能、持久会话和复杂任务编排的状态代理的强大框架时,请选择 Julep。 + +
+ + Back to Top +  |  + + Table of Contents + +
diff --git a/README-FR.md b/README-FR.md new file mode 100644 index 000000000..21746c979 --- /dev/null +++ b/README-FR.md @@ -0,0 +1,1680 @@ +[English](README.md) | [中文翻译](README-CN.md) | [日本語翻訳](README-JA.md) | [French](README-FR.md) + +
+ julep + julep +
+ +

+
+ Explorer les documents (en cours) + Explorer les documents (en cours) + · + Discorde + · + 𝕏 + · + LinkedIn +

+ +

+ NPM Version +   + PyPI - Version +   + Docker Image Version +   + GitHub License +

+ +--- + +> [!REMARQUE] +> Obtenez votre clé API [ici](https://dashboard-dev.julep.ai). + +
+Contributions 🌟(Cliquez pour agrandir) + +## Appel aux contributeurs 🌟 + +Nous sommes ravis d'accueillir de nouveaux contributeurs au projet Julep ! Nous avons créé plusieurs « bons premiers numéros » pour vous aider à démarrer. Voici comment vous pouvez contribuer : + +1. Consultez notre fichier [CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/CONTRIBUTING.md) pour obtenir des instructions sur la façon de contribuer. +2. Parcourez nos [bons premiers numéros](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) pour trouver une tâche qui vous intéresse. +3. Si vous avez des questions ou avez besoin d'aide, n'hésitez pas à nous contacter sur notre chaîne [Discord](https://discord.com/invite/JTSBGRZrzj). + +Vos contributions, grandes ou petites, sont précieuses pour nous. Construisons ensemble quelque chose d'extraordinaire ! 🚀 + +
+ + + +

📖 Table des matières

+

📖 Table des matières

+ +- [Appel aux contributeurs 🌟](#appel-aux-contributeurs-) +- [Introduction](#introduction) +- [Principales caractéristiques](#principales-caractéristiques) +- [Exemple rapide](#exemple-rapide) +- [Installation](#installation) +- [Démarrage rapide de Python 🐍](#démarrage-rapide-de-python-) +- [Node.js Quick Start 🟩](#nodejs-quick-start-) +- [Components](#components) + - [Mental Model](#mental-model) +- [Concepts](#concepts) +- [Understanding Tasks](#understanding-tasks) + - [Lifecycle of a Task](#lifecycle-of-a-task) + - [Types of Workflow Steps](#types-of-workflow-steps) + - [Common Steps](#common-steps) + - [Key-Value Steps](#key-value-steps) + - [Iteration Steps](#iteration-steps) + - [Conditional Steps](#conditional-steps) + - [Other Control Flow](#other-control-flow) +- [Tool Types](#tool-types) + - [User-defined `functions`](#user-defined-functions) + - [`system` tools](#system-tools) + - [Available `system` resources and operations](#available-system-resources-and-operations) + - [Built-in `integrations`](#built-in-integrations) + - [Direct `api_calls`](#direct-api_calls) +- [Integrations](#integrations) +- [Other Features](#other-features) + - [Adding Tools to Agents](#adding-tools-to-agents) + - [Managing Sessions and Users](#managing-sessions-and-users) + - [Document Integration and Search](#document-integration-and-search) +- [Référence](#référence) + - [Référence du SDK](#référence-du-sdk) + - [Référence API](#référence-api) +- [Démarrage rapide local](#démarrage-rapide-local) +- [Quelle est la différence entre Julep et LangChain etc ?](#quelle-est-la-différence-entre-julep-et-langchain-etc-) + - [Différents cas d'utilisation](#différents-cas-dutilisation) + - [Facteur de forme différent](#facteur-de-forme-différent) + - [En résumé](#en-résumé) + + + +## Introduction + +Julep est une plateforme permettant de créer des agents IA qui se souviennent des interactions passées et peuvent effectuer des tâches complexes. Elle offre une mémoire à long terme et gère des processus en plusieurs étapes. + +Julep permet la création de tâches en plusieurs étapes intégrant la prise de décision, les boucles, le traitement parallèle et l'intégration avec de nombreux outils et API externes. + +Alors que de nombreuses applications d'IA se limitent à des chaînes simples et linéaires d'invites et d'appels d'API avec une ramification minimale, Julep est conçu pour gérer des scénarios plus complexes qui : + +- comporter plusieurs étapes, +- prendre des décisions basées sur les résultats du modèle, +- générer des branches parallèles, +- utiliser beaucoup d'outils, et +- courir pendant une longue période. + +> [!TIP] +> Imaginez que vous souhaitiez créer un agent d'IA capable de faire plus que simplement répondre à des questions simples : il doit gérer des tâches complexes, mémoriser des interactions passées et peut-être même utiliser d'autres outils ou API. C'est là qu'intervient Julep. Lisez [Comprendre les tâches](#understanding-tasks) pour en savoir plus. + +## Principales caractéristiques + +1. 🧠 **Agents IA persistants** : mémorisent le contexte et les informations au cours d'interactions à long terme. +2. 💾 **Sessions avec état** : gardez une trace des interactions passées pour des réponses personnalisées. +3. 🔄 **Tâches en plusieurs étapes** : créez des processus complexes en plusieurs étapes avec des boucles et une prise de décision. +4. ⏳ **Gestion des tâches** : gérez les tâches de longue durée qui peuvent s'exécuter indéfiniment. +5. 🛠️ **Outils intégrés** : utilisez des outils intégrés et des API externes dans vos tâches. +6. 🔧 **Auto-réparation** : Julep réessaiera automatiquement les étapes ayant échoué, renverra les messages et assurera généralement le bon déroulement de vos tâches. +7. 📚 **RAG** ​​: Utilisez le magasin de documents de Julep pour créer un système permettant de récupérer et d'utiliser vos propres données. + +![fonctionnalités](https://github.com/user-attachments/assets/4355cbae-fcbd-4510-ac0d-f8f77b73af70) + +> [!TIP] +> Julep est idéal pour les applications qui nécessitent des cas d’utilisation de l’IA au-delà des simples modèles de réponse rapide. + +## Exemple rapide + +Imaginez un agent d’IA de recherche capable d’effectuer les opérations suivantes : + +1. **Prenez un sujet**, +2. **Proposez 30 requêtes de recherche** pour ce sujet, +3. Effectuez ces **recherches Web en parallèle**, +4. **Résumez** les résultats, +5. Envoyez le **résumé à Discord**. + +> [!REMARQUE] +> Dans Julep, ce serait une tâche unique sous80 lignes de codeet courirentièrement gérétout seul. Toutes les étapes sont exécutées sur les propres serveurs de Julep et vous n'avez pas besoin de lever le petit doigt. + +Voici un exemple fonctionnel : + +```yaml +name: Research Agent + +# Optional: Define the input schema for the task +input_schema: + type: object + properties: + topic: + type: string + description: The main topic to research + num_questions: + type: integer + description: The number of search queries to generate + +# Define the tools that the agent can use +tools: + - name: web_search + type: integration + integration: + provider: brave + setup: + api_key: + + - name: discord_webhook + type: api_call + api_call: + url: https://discord.com/api/webhooks// + method: POST + headers: + Content-Type: application/json + +# Special variables: +# - inputs: for accessing the input to the task +# - outputs: for accessing the output of previous steps +# - _: for accessing the output of the previous step + +# Define the main workflow +main: +- prompt: + - role: system + content: >- + You are a research assistant. + Generate {{inputs[0].num_questions|default(30, true)}} diverse search queries related to the topic: + {{inputs[0].topic}} + + Write one query per line. + unwrap: true + +# Evaluate the search queries using a simple python expression +- evaluate: + search_queries: "_.split(NEWLINE)" + +# Run the web search in parallel for each query +- over: "_.search_queries" + map: + tool: web_search + arguments: + query: "_" + parallelism: 5 + +# Collect the results from the web search +- evaluate: + search_results: _ + +# Summarize the results +- prompt: + - role: system + content: > + You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. + The summary should be well-structured, informative, and highlight key findings and insights. Keep the summary concise and to the point. + The length of the summary should be less than 150 words. + Here are the search results: + {{_.search_results}} + unwrap: true + settings: + model: gpt-4o-mini + +- evaluate: + discord_message: |- + f''' + **Research Summary for {inputs[0].topic}** + {_} + ''' + + # Send the summary to Discord +- tool: discord_webhook + arguments: + json_: + content: _.discord_message[:2000] # Discord has a 2000 character limit +``` + +Dans cet exemple, Julep gérera automatiquement les exécutions parallèles, réessayera les étapes ayant échoué, renverra les requêtes API et maintiendra les tâches en cours d'exécution de manière fiable jusqu'à leur achèvement. + +> Cela s'exécute en moins de 30 secondes et renvoie le résultat suivant : + +
+Résumé de la recherche sur l'IA (Cliquez pour agrandir) + +> **Résumé de la recherche sur l'IA** +> +> ### Résumé des résultats de recherche sur l'intelligence artificielle (IA) +> +> #### Présentation +> +> Le domaine de l’intelligence artificielle (IA) a connu des avancées significatives ces dernières années, marquées par le développement de méthodes et de technologies permettant aux machines de percevoir leur environnement, d’apprendre à partir de données et de prendre des décisions. L’objectif principal de ce résumé est de présenter les enseignements tirés de divers résultats de recherche liés à l’IA. +> +> #### Principales conclusions +> +> 1. **Définition et portée de l’IA** : +> +> - L'IA est définie comme une branche de l'informatique axée sur la création de systèmes capables d'effectuer des tâches nécessitant une intelligence humaine, notamment l'apprentissage, le raisonnement et la résolution de problèmes (Wikipedia). +> - Il englobe divers sous-domaines, notamment l’apprentissage automatique, le traitement du langage naturel, la robotique et la vision par ordinateur. +> +> 2. **Impact et applications** : +> +> - Les technologies d'IA sont intégrées dans de nombreux secteurs, améliorant l'efficacité et la productivité. Les applications vont des véhicules autonomes et des diagnostics de santé à l'automatisation du service client et aux prévisions financières (OpenAI). +> - L'engagement de Google à rendre l'IA bénéfique pour tous met en évidence son potentiel à améliorer considérablement la vie quotidienne en améliorant l'expérience utilisateur sur diverses plateformes (Google AI). +> +> 3. **Considérations éthiques** : +> +> - Un débat est en cours sur les implications éthiques de l'IA, notamment sur les préoccupations relatives à la confidentialité, aux préjugés et à la responsabilité dans les processus de prise de décision. La nécessité d'un cadre garantissant l'utilisation sûre et responsable des technologies de l'IA est soulignée (OpenAI). +> +> 4. **Mécanismes d’apprentissage** : +> +> - Les systèmes d'IA utilisent différents mécanismes d'apprentissage, tels que l'apprentissage supervisé, l'apprentissage non supervisé et l'apprentissage par renforcement. Ces méthodes permettent à l'IA d'améliorer ses performances au fil du temps en apprenant des expériences et des données passées (Wikipedia). +> - La distinction entre l’apprentissage supervisé et non supervisé est essentielle ; l’apprentissage supervisé s’appuie sur des données étiquetées, tandis que l’apprentissage non supervisé identifie des modèles sans étiquettes prédéfinies (non supervisé). +> +> 5. **Orientations futures**: +> - Les futurs développements de l’IA devraient se concentrer sur l’amélioration de l’interprétabilité et de la transparence des systèmes d’IA, garantissant qu’ils peuvent fournir des décisions et des actions justifiables (OpenAI). +> - On observe également une volonté de rendre les systèmes d’IA plus accessibles et plus conviviaux, encourageant une adoption plus large dans différents groupes démographiques et secteurs (Google AI). +> +> #### Conclusion +> +> L’IA représente une force de transformation dans de nombreux domaines, promettant de remodeler les industries et d’améliorer la qualité de vie. Cependant, à mesure que ses capacités se développent, il est essentiel de tenir compte des implications éthiques et sociétales qui en découlent. La poursuite des recherches et de la collaboration entre les technologues, les éthiciens et les décideurs politiques sera essentielle pour s’orienter dans le futur paysage de l’IA. + +
+ +## Installation + +Pour commencer à utiliser Julep, installez-le en utilisant [npm](https://www.npmjs.com/package/@julep/sdk) ou [pip](https://pypi.org/project/julep/) : + +**Node.js**: + +```bash +npm install @julep/sdk + +# or + +bun add @julep/sdk +``` + +**Python**: + +```bash +pip install julep +``` + +> [!REMARQUE] +> Obtenez votre clé API [ici](https://dashboard-dev.julep.ai). +> +> Pendant que nous sommes en version bêta, vous pouvez également nous contacter sur [Discord](https://discord.com/invite/JTSBGRZrzj) pour obtenir la levée des limites de débit sur votre clé API. + +> [!TIP] +> 💻 Êtes-vous du genre à vouloir _montrer le code !™_ ? Nous avons créé une multitude de livres de recettes pour vous aider à démarrer. **Consultez les [livres de recettes](https://github.com/julep-ai/julep/tree/dev/cookbooks)** pour parcourir les exemples. +> +> 💡 Il existe également de nombreuses idées que vous pouvez développer en plus de Julep. **Consultez la [liste d'idées](https://github.com/julep-ai/julep/tree/dev/cookbooks/IDEAS.md)** pour vous inspirer. + +## Démarrage rapide de Python 🐍 + +````python +### Step 0: Setup + +import time +import yaml +from julep import Julep # or AsyncJulep + +client = Julep(api_key="your_julep_api_key") + +### Step 1: Create an Agent + +agent = client.agents.create( + name="Storytelling Agent", + model="claude-3.5-sonnet", + about="You are a creative storyteller that crafts engaging stories on a myriad of topics.", +) + +### Step 2: Create a Task that generates a story and comic strip + +task_yaml = """ +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + type: integration + integration: + provider: wikipedia + method: search + +main: + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside ```balises yaml à la fin de votre réponse. +déballer : vrai + +- évaluer: +plot_ideas : load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + +# Étape 2 : Extraire les domaines de recherche des idées de l'intrigue +- rapide: +- rôle : système +contenu : Vous êtes {{agent.name}}. {{agent.about}} +- rôle : utilisateur +contenu : > +Voici quelques idées d’intrigue pour une histoire : +{% pour l'idée dans _.plot_ideas %} +- {{idée}} +{% fin de %} + +Pour développer l’histoire, nous devons rechercher les idées d’intrigue. +Sur quoi devrions-nous faire des recherches ? Notez les requêtes de recherche Wikipédia pour les idées d'intrigue que vous trouvez intéressantes. +Renvoyez votre sortie sous forme de liste yaml à l'intérieur```yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 + + - evaluate: + research_queries: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + + # Step 3: Research each plot idea + - foreach: + in: _.research_queries + do: + tool: research_wikipedia + arguments: + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + + # Step 4: Think and deliberate + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside ```balises yaml à la fin de votre réponse. L'objet yaml doit avoir la structure suivante : + + ```yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""``` + +Assurez-vous que le fichier YAML est valide et que les caractères et les scènes ne sont pas vides. Faites également attention aux points-virgules et autres problèmes liés à l'écriture du fichier YAML. +déballer : vrai + +- évaluer: +intrigue : « load_yaml(_.split('```yaml')[1].split('```')[0].strip())" +""" + +tâche = client.tasks.create( +agent_id=agent.id, +**yaml.safe_load(tâche_yaml) +) + +### Étape 3 : Exécuter la tâche + +exécution = client.executions.create( +task_id=task.id, +input={"idea": "Un chat qui apprend à voler"} +) + +# 🎉 Regardez l'histoire et les panneaux de bandes dessinées se générer +while (result := client.executions.get(execution.id)).status n'est pas dans ['réussi', 'échec'] : +print(résultat.statut, résultat.sortie) +heure.sommeil(1) + +# 📦 Une fois l'exécution terminée, récupérez les résultats +si result.status == "réussi" : +imprimer(résultat.sortie) +autre: +déclencher une exception (résultat.erreur) +```` + +You can find the full python example [here](example.py). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Node.js Quick Start 🟩 + +```javascript +// Étape 0 : Configuration +const dotenv = require("dotenv"); +const { Julep } = require("@julep/sdk"); +const yaml = require("yaml"); + +dotenv.config(); + +const client = nouveau Julep({ +Clé API : processus.env.JULEP_API_KEY, +environnement : process.env.JULEP_ENVIRONMENT || "production", +}); + +/* Étape 1 : Créer un agent */ + +fonction asynchrone createAgent() { +agent constant = attendez que le client.agents.create({ +nom : « Agent de narration », +modèle : "claude-3.5-sonnet", +à propos de: +« Vous êtes un conteur créatif qui crée des histoires captivantes sur une myriade de sujets. », + }); +agent de retour; +} + +/* Étape 2 : Créer une tâche qui génère une histoire et une bande dessinée */ + +const tâcheYaml = ` +nom : Conteur +description : Créez une histoire basée sur une idée. + +outils: +- nom : research_wikipedia +intégration: +fournisseur : wikipedia +méthode : recherche + +principal: +# Étape 1 : Générer une idée d'intrigue +- rapide: +- rôle : système +contenu : Vous êtes {{agent.name}}. {{agent.about}} +- rôle : utilisateur +contenu : > +En vous basant sur l'idée « {{_.idea}} », générez une liste de 5 idées d'intrigue. Laissez libre cours à votre créativité. Renvoyez votre résultat sous forme de liste de longues chaînes à l'intérieur des balises \`\`\`yaml à la fin de votre réponse. +déballer : vrai + +- évaluer: +plot_ideas: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# Étape 2 : Extraire les domaines de recherche des idées de l'intrigue +- rapide: +- rôle : système +contenu : Vous êtes {{agent.name}}. {{agent.about}} +- rôle : utilisateur +contenu : > +Voici quelques idées d’intrigue pour une histoire : +{% pour l'idée dans _.plot_ideas %} +- {{idée}} +{% fin de %} + +Pour développer l’histoire, nous devons rechercher les idées d’intrigue. +Sur quoi devrions-nous faire des recherches ? Notez les requêtes de recherche Wikipédia pour les idées d'intrigue que vous trouvez intéressantes. +Renvoyez votre sortie sous forme de liste yaml à l'intérieur des balises \`\`\`yaml à la fin de votre réponse. +déballer : vrai +paramètres: +modèle: gpt-4o-mini +température: 0,7 + +- évaluer: +requêtes de recherche : load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# Étape 3 : Recherchez chaque idée d'intrigue +- pour chaque : +dans : _.research_queries +faire: +outil : research_wikipedia +Arguments: +requête: _ + +- évaluer: +wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" pour l'élément dans _ pour le document dans l'élément.documents])' + +# Étape 4 : Réfléchir et délibérer +- rapide: +- rôle : système +contenu : Vous êtes {{agent.name}}. {{agent.about}} +- rôle : utilisateur +contenu: |- +Avant d'écrire l'histoire, réfléchissons et délibérons. Voici quelques idées d'intrigue : +{% pour l'idée dans les sorties[1].plot_ideas %} +- {{idée}} +{% fin de %} + +Voici les résultats de la recherche d'idées d'intrigue sur Wikipédia : +{{_.wikipedia_results}} + +Réfléchissez aux idées de l'intrigue de manière critique. Combinez les idées de l'intrigue avec les résultats de Wikipédia pour créer une intrigue détaillée pour une histoire. +Écrivez toutes vos notes et vos pensées. +Ensuite, écrivez enfin le tracé sous forme d'objet yaml à l'intérieur des balises \`\`\`yaml à la fin de votre réponse. L'objet yaml doit avoir la structure suivante : + +\`\`\`yaml +titre: "" +personnages: +- nom: "" +à propos de: "" +résumé: "" +scènes: +- titre: "" +description: "" +personnages: +- nom: "" +rôle: "" +intrigues: + - ""\`\`\` + +Assurez-vous que le fichier YAML est valide et que les caractères et les scènes ne sont pas vides. Faites également attention aux points-virgules et autres problèmes liés à l'écriture du fichier YAML. +déballer : vrai + +- évaluer: +tracé : « load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) » +`; + +fonction asynchrone createTask(agentId) { +const tâche = wait client.tasks.create(agentId, yaml.parse(taskYaml)); +tâche de retour; +} + +/* Étape 3 : Exécuter la tâche */ + +fonction asynchrone executeTask(taskId) { +const exécution = attendre client.executions.create(taskId, { +entrée : { idée : "Un chat qui apprend à voler" }, + }); + +// 🎉 Regardez comment l'histoire et les panneaux de bande dessinée sont générés +tandis que (vrai) { +const résultat = wait client.executions.get(execution.id); +console.log(résultat.status, résultat.output); + +si (résultat.status === "réussi" || résultat.status === "échec") { +// 📦 Une fois l'exécution terminée, récupérez les résultats +si (résultat.status === "réussi") { +console.log(résultat.sortie); +} autre { +lancer une nouvelle erreur (résultat.erreur); + } +casser; + } + +attendre une nouvelle promesse((résolution) => setTimeout(résolution, 1000)); + } +} + +// Fonction principale pour exécuter l'exemple +fonction asynchrone main() { +essayer { +agent constant = wait createAgent(); +const tâche = wait createTask(agent.id); +attendre executeTask(task.id); +} catch (erreur) { +console.error("Une erreur s'est produite :", error); + } +} + +principal() +.then(() => console.log("Terminé")) +.catch(console.erreur); +``` + +You can find the full Node.js example [here](example.js). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Components + +Julep is made up of the following components: + +- **Julep Platform**: The Julep platform is a cloud service that runs your workflows. It includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. +- **Julep SDKs**: Julep SDKs are a set of libraries for building workflows. There are SDKs for Python and JavaScript, with more on the way. +- **Julep API**: The Julep API is a RESTful API that you can use to interact with the Julep platform. + +### Mental Model + +
+ +
+ +Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's how to visualize it: + +1. **Your Application Code:** + + - You can use the Julep SDK in your application to define agents, tasks, and workflows. + - The SDK provides functions and classes that make it easy to set up and manage these components. + +2. **Julep Backend Service:** + + - The SDK communicates with the Julep backend over the network. + - The backend handles execution of tasks, maintains session state, stores documents, and orchestrates workflows. + +3. **Integration with Tools and APIs:** + - Within your workflows, you can integrate external tools and services. + - The backend facilitates these integrations, so your agents can, for example, perform web searches, access databases, or call third-party APIs. + +## Concepts + +Julep is built on several key technical components that work together to create powerful AI workflows: + +```sirène +graphique TD +Utilisateur[Utilisateur] ==> Session[Session] +Session --> Agent[Agent] +Agent --> Tâches[Tâches] +Agent --> LLM [Modèle de langage étendu] +Tâches --> Outils[Outils] +Agent --> Documents[Documents] +Documents --> VectorDB[Base de données vectorielles] +Tâches --> Exécutions[Exécutions] + +client classDef remplissage : #9ff, trait : #333, largeur du trait : 1 px ; +classe Utilisateur client ; + +classDef core fill:#f9f,trait:#333,largeur-trait:2px; +classe Agent,Tâches,Session core; +``` + +- **Agents**: AI-powered entities backed by large language models (LLMs) that execute tasks and interact with users. +- **Users**: Entities that interact with agents through sessions. +- **Sessions**: Stateful interactions between agents and users, maintaining context across multiple exchanges. +- **Tasks**: Multi-step, programmatic workflows that agents can execute, including various types of steps like prompts, tool calls, and conditional logic. +- **Tools**: Integrations that extend an agent's capabilities, including user-defined functions, system tools, or third-party API integrations. +- **Documents**: Text or data objects associated with agents or users, vectorized and stored for semantic search and retrieval. +- **Executions**: Instances of tasks that have been initiated with specific inputs, with their own lifecycle and state machine. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Understanding Tasks + +Tasks are the core of Julep's workflow system. They allow you to define complex, multi-step AI workflows that your agents can execute. Here's a brief overview of task components: + +- **Name, Description and Input Schema**: Each task has a unique name and description for easy identification. An input schema (optional) that is used to validate the input to the task. +- **Main Steps**: The core of a task, defining the sequence of actions to be performed. Each step can be a prompt, tool call, evaluate, wait_for_input, log, get, set, foreach, map_reduce, if-else, switch, sleep, or return. (See [Types of Workflow Steps](#types-of-workflow-steps) for more details) +- **Tools**: Optional integrations that extend the capabilities of your agent during task execution. + +### Lifecycle of a Task + +You create a task using the Julep SDK and specify the main steps that the agent will execute. When you execute a task, the following lifecycle happens: + +```sirène +Diagramme de séquence +participant D comme votre code +participant C en tant que client Julep +participant S en tant que serveur Julep + +D->>C : Créer une tâche +C->>S : Soumettre l'exécution +Remarque sur S : Exécuter la tâche +Remarque sur S : Gérer l'état +S-->>C : Événements d'exécution +C-->>D : Mises à jour de la progression +S->>C : Fin de l'exécution +C->>D : Résultat final +``` + +### Types of Workflow Steps + +Tasks in Julep can include various types of steps, allowing you to create complex and powerful workflows. Here's an overview of the available step types: + +#### Common Steps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAboutSyntax
Prompt +Send a message to the AI model and receive a response +

Note: The prompt step uses Jinja templates and you can access context variables in them. +
+ +```YAML +- invite : « Analyser les données suivantes : {{agent.name}} » # <-- ceci est un modèle jinja +``` + +```YAML +- rapide: +- rôle : système +contenu : « Vous êtes {{agent.name}}. {{agent.about}} » +- rôle : utilisateur +contenu : « Analysez les données suivantes : {{_.data}} » +``` + +
Tool Call +Execute an integrated tool or API that you have previously declared in the task. +

Note: The tool call step uses Python expressions inside the arguments. + +
+ +```YAML +- outil : recherche_sur_le_web +Arguments: +requête : « Derniers développements de l'IA » # <-- il s'agit d'une expression Python (remarquez les guillemets) +num_results: len(_.topics) # <-- expression python pour accéder à la longueur d'une liste +``` + +
Evaluate +Perform calculations or manipulate data +

Note: The evaluate step uses Python expressions. +
+ +```YAML +- évaluer: +average_score : somme(scores) / len(scores) +``` + +
Wait for Input +Pause workflow until input is received. It accepts an `info` field that can be used by your application to collect input from the user. + +

Note: The wait_for_input step is useful when you want to pause the workflow and wait for user input e.g. to collect a response to a prompt. + +
+ +```YAML +- attendre_la_saisie : +info: +message : « Veuillez fournir des informations supplémentaires sur {_.required_info}. » # <-- expression Python pour accéder à la variable de contexte +``` + +
Log +Log a specified value or message. + +

Note: The log step uses Jinja templates and you can access context variables in them. + +
+ +```YAML +- log : « Traitement terminé pour l'élément {{_.item_id}} » # <-- modèle jinja pour accéder à la variable de contexte +``` + +
+ +#### Key-Value Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
Get +Retrieve a value from the execution's key-value store. + + + +```YAML +- obtenir : préférences_utilisateur +``` + +
Set +Assign a value to a key in the execution's key-value store. + +

Note: The set step uses Python expressions. + +
+ +```YAML +- ensemble: +préférence_utilisateur : '"dark_mode"' # <-- expression python +``` + +
+ +#### Iteration Steps + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Foreach +Iterate over a collection and perform steps for each item + + + +```YAML +- pour chaque : +dans : _.data_list # <-- expression python pour accéder à la variable de contexte +faire: +- log : « Traitement de l'élément {{_.item}} » # <-- modèle jinja pour accéder à la variable de contexte +``` + +
Map-Reduce +Map over a collection and reduce the results + + + +```YAML +- map_reduce: +over: _.numbers # <-- expression python pour accéder à la variable de contexte +carte: +- évaluer: +au carré : "_ ** 2" +réduire : résultats + [_] # <-- (facultatif) expression Python pour réduire les résultats. Il s'agit de la valeur par défaut si elle est omise. +``` + +```YAML +- map_reduce: +plus de: _.topics +carte: +- invite : Rédigez un essai sur {{_}} +parallélisme : 10 +``` + +
Parallel +Run multiple steps in parallel + + + +```YAML +- parallèle: +- outil : recherche_sur_le_web +Arguments: +requête : « Actualités sur l'IA » +- outil : weather_check +Arguments: +Lieu : « New York » +``` + +
+ +#### Conditional Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
If-Else +Conditional execution of steps + + + +```YAML +- si : _.score > 0.8 # <-- expression python +alors: +- log : score élevé atteint +autre: +- erreur : le score doit être amélioré +``` + +
Switch +Execute steps based on multiple conditions + + + +```YAML +- changer: +- cas : _.category == 'A' +alors: +- log : « Traitement de catégorie A » +- cas : _.category == 'B' +alors: +- log : « Traitement de catégorie B » +- case: _ # Cas par défaut +alors: +- erreur : catégorie inconnue +``` + +
+ +#### Other Control Flow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Sleep +Pause the workflow for a specified duration + + + +```YAML +- dormir: +secondes: 30 +# minutes: 1 +# heures: 1 +# jours: 1 +``` + +
Return +Return a value from the workflow + +

Note: The return step uses Python expressions. + +
+ +```YAML +- retour: +résultat : " Tâche terminée avec succès " # <-- expression python +heure : datetime.now().isoformat() # <-- expression python +``` + +
Yield +Run a subworkflow and await its completion + + + +```YAML +- rendement: +flux de travail : données_de_processus +Arguments: +données_d'entrée : _. données_raw # <-- expression python +``` + +
Error +Handle errors by specifying an error message + + + +```YAML +- erreur : « Entrée non valide fournie » # <-- Chaînes uniquement +``` + +
+ +Each step type serves a specific purpose in building sophisticated AI workflows. This categorization helps in understanding the various control flows and operations available in Julep tasks. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Tool Types + +Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a `web_search(query)` tool to search the Internet for some information. + +Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. + +Tools in julep can be one of: + +1. **User-defined `functions`**: These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. They need to be handled by the client. The workflow will pause until the client calls the function and gives the results back to julep. +2. **`system` tools**: Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. +3. **`integrations`**: Built-in third party tools that can be used to extend the capabilities of your agents. +4. **`api_calls`**: Direct api calls during workflow executions as tool calls. + +### User-defined `functions` + +These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + +```YAML +nom : Exemple de tâche d'outil système +description : Lister les agents à l'aide d'un appel système + +outils: +- nom : send_notification +description : Envoyer une notification à l'utilisateur +type : fonction +fonction: +paramètres: +type: objet +propriétés: +texte: +type : chaîne +description : Contenu de la notification + +principal: +- outil : send_notification +Arguments: +contenu : '"salut"' # <-- expression python +``` + +Whenever julep encounters a _user-defined function_, it pauses, giving control back to the client and waits for the client to run the function call and give the results back to julep. + +### `system` tools + +Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + +`system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + +For example, + +```YAML +nom : Exemple de tâche d'outil système +description : Lister les agents à l'aide d'un appel système + +outils: +- nom : list_agent_docs +description : liste tous les documents pour l'agent donné +type : système +système: +ressource : agent +sous-ressource : doc +opération : liste + +principal: +- outil : list_agents +Arguments: +limite : 10 # <-- expression python +``` + +#### Available `system` resources and operations + +- `agent`: + + - `list`: List all agents. + - `get`: Get a single agent by id. + - `create`: Create a new agent. + - `update`: Update an existing agent. + - `delete`: Delete an existing agent. + +- `user`: + + - `list`: List all users. + - `get`: Get a single user by id. + - `create`: Create a new user. + - `update`: Update an existing user. + - `delete`: Delete an existing user. + +- `session`: + + - `list`: List all sessions. + - `get`: Get a single session by id. + - `create`: Create a new session. + - `update`: Update an existing session. + - `delete`: Delete an existing session. + - `chat`: Chat with a session. + - `history`: Get the chat history with a session. + +- `task`: + + - `list`: List all tasks. + - `get`: Get a single task by id. + - `create`: Create a new task. + - `update`: Update an existing task. + - `delete`: Delete an existing task. + +- `doc` (subresource for `agent` and `user`): + - `list`: List all documents. + - `create`: Create a new document. + - `delete`: Delete an existing document. + - `search`: Search for documents. + +Additional operations available for some resources: + +- `embed`: Embed a resource (specific resources not specified in the provided code). +- `change_status`: Change the status of a resource (specific resources not specified in the provided code). +- `chat`: Chat with a resource (specific resources not specified in the provided code). +- `history`: Get the chat history with a resource (specific resources not specified in the provided code). +- `create_or_update`: Create a new resource or update an existing one (specific resources not specified in the provided code). + +Note: The availability of these operations may vary depending on the specific resource and implementation details. + +> [!TIP] > **Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +### Built-in `integrations` + +Julep comes with a number of built-in integrations (as described in the section below). `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + +See [Integrations](#integrations) for details on the available integrations. + +> [!TIP] > **Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +### Direct `api_calls` + +julep can also directly make api calls during workflow executions as tool calls. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + +For example, + +```YAML +nom : Exemple de tâche api_call +outils: +- type : api_call +nom : bonjour +appel_API : +méthode : GET +URL: https://httpbin.org/get + +principal: +- outil : bonjour +Arguments: +json: +test: _.input # <-- expression python +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Integrations + +Julep supports various integrations that extend the capabilities of your AI agents. Here's a list of available integrations and their supported arguments: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Brave Search + +```YAML +installation: +api_key : chaîne # La clé API pour Brave Search + +Arguments: +requête : chaîne # La requête de recherche pour rechercher avec Brave + +sortir: +résultat : chaîne # Le résultat de la recherche Brave +``` + + + +**Example cookbook**: [cookbooks/02-sarcastic-news-headline-generator.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/02-sarcastic-news-headline-generator.ipynb) + +
BrowserBase + +```YAML +installation: +api_key : chaîne # La clé API pour BrowserBase +project_id : chaîne # L'ID de projet pour BrowserBase +session_id : chaîne # (facultatif) L'ID de session pour BrowserBase + +Arguments: +urls : liste[chaîne] # Les URL pour le chargement avec BrowserBase + +sortir: +documents : liste # Les documents chargés à partir des URL +``` + + + +**Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +
Email + +```YAML +installation: +hôte : chaîne # L'hôte du serveur de messagerie +port : entier # Le port du serveur de messagerie +utilisateur : chaîne # Le nom d'utilisateur du serveur de messagerie +mot de passe : chaîne # Le mot de passe du serveur de messagerie + +Arguments: +à : chaîne # L'adresse e-mail à laquelle envoyer l'e-mail +de : chaîne # L'adresse e-mail à partir de laquelle envoyer l'e-mail +objet : chaîne # L'objet de l'e-mail +corps : chaîne # Le corps de l'e-mail + +sortir: +succès : booléen # Indique si l'e-mail a été envoyé avec succès +``` + + + +**Example cookbook**: [cookbooks/00-Devfest-Email-Assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/00-Devfest-Email-Assistant.ipynb) + +
Spider + +```YAML +installation: +spider_api_key : chaîne # La clé API pour Spider + +Arguments: +url : chaîne # L'URL pour laquelle récupérer les données +mode : chaîne # Le type de robots d'exploration (par défaut : « scrape ») +paramètres : dict # (facultatif) Les paramètres de l'API Spider + +sortir: +documents : liste # Les documents renvoyés par l'araignée +``` + + + +**Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +
Weather + +```YAML +installation: +openweathermap_api_key : chaîne # La clé API pour OpenWeatherMap + +Arguments: +emplacement : chaîne # L'emplacement pour lequel récupérer les données météorologiques + +sortir: +résultat : chaîne # Les données météorologiques pour l'emplacement spécifié +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
Wikipedia + +```YAML +Arguments: +requête : chaîne # La chaîne de requête de recherche +load_max_docs : entier # (facultatif) Nombre maximal de documents à charger. La valeur par défaut est 2. + +sortir: +documents : liste # Les documents renvoyés par la recherche sur Wikipédia +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
FFmpeg + +```YAML +Arguments: +cmd : chaîne # La commande FFmpeg à exécuter +fichier : chaîne # Le fichier codé en base64 à traiter + +sortir: +fileoutput : chaîne # Le fichier de sortie de la commande FFmpeg en codage base64 +résultat : booléen # Si la commande FFmpeg a été exécutée avec succès +mime_type : chaîne # Le type MIME du fichier de sortie +``` + +
Llama Parse + +```YAML +installation: +llamaparse_api_key: string # La clé API pour Llama Parse +paramètres : dict # (facultatif) Paramètres supplémentaires pour l'intégration de Llama Parse + +Arguments: +fichier : chaîne | tableau# Le fichier codé en base64 à analyser ou un tableau d'URL http/https à charger. +nom de fichier : chaîne # (facultatif). Le nom de fichier du fichier. La valeur par défaut est un UUID aléatoire. Utilisé uniquement si le fichier est une chaîne codée en base64. +params : dict # (facultatif) Paramètres supplémentaires pour l'intégration de Llama Parse. Remplace les paramètres de configuration. +base64 : booléen # Indique si le fichier d'entrée est encodé en base64. La valeur par défaut est false. + +sortir: +documents : liste # Les données analysées du document +``` + +
Cloudinary + +```YAML + +méthode : media_upload | media_edit # La méthode à utiliser pour l'intégration Cloudinary + +installation: +cloudinary_cloud_name : chaîne # Votre nom de cloud Cloudinary +cloudinary_api_key : chaîne # Votre clé API Cloudinary +cloudinary_api_secret : chaîne # Votre secret d'API Cloudinary +paramètres : dict # (facultatif) Paramètres supplémentaires pour l'intégration Cloudinary + +Arguments: +fichier : chaîne # L'URL du téléchargement du fichier. Disponible uniquement pour la méthode media_upload. +upload_params : dict # (facultatif) Paramètres supplémentaires pour le téléchargement. Disponible uniquement pour la méthode media_upload. +public_id : chaîne # (facultatif) L'ID public du fichier. Pour la méthode media_edit, il est OBLIGATOIRE. Pour la méthode media_upload, il est facultatif. La valeur par défaut est un UUID aléatoire. +transformation : list[dict] # Les transformations à appliquer au fichier. Disponible uniquement pour la méthode media_edit. +return_base64 : booléen # Indique si le fichier doit être renvoyé en codage base64. La valeur par défaut est false. + +sortir: +url : chaîne # L'URL du fichier téléchargé. Disponible uniquement pour la méthode media_upload. +meta_data : dict # Métadonnées supplémentaires provenant de la réponse de téléchargement. Disponible uniquement pour la méthode media_upload. +public_id : chaîne # L'ID public du fichier téléchargé. Disponible uniquement pour la méthode media_upload. +turned_url : chaîne # (facultatif) L'URL transformée. Disponible uniquement pour la méthode media_edit. +base64 : chaîne # (Facultatif) Le fichier codé en base64 si return_base64 est vrai. +``` + + + +**Example cookbook**: [cookbooks/05-video-processing-with-natural-language.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/05-video-processing-with-natural-language.ipynb) + +
+ +For more details, refer to our [Integrations Documentation](#integrations). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Other Features + +Julep offers a range of advanced features to enhance your AI workflows: + +### Adding Tools to Agents + +Extend your agent's capabilities by integrating external tools and APIs: + +```python +client.agents.outils.créer( +agent_id=agent.id, +nom="recherche_sur_le_web", +description="Rechercher des informations sur le Web.", +intégration={ +"fournisseur": "courageux", +"méthode": "recherche", +"setup": {"api_key": "votre_brave_api_key"}, + }, +) +``` + +### Managing Sessions and Users + +Julep provides robust session management for persistent interactions: + +```python +session = client.sessions.create( +agent_id=agent.id, +user_id=utilisateur.id, +context_overflow="adaptatif" +) + +# Poursuivre la conversation dans la même session +réponse = client.sessions.chat( +session_id=session.id, +messages=[ + { +"rôle": "utilisateur", +« contenu » : « Suivi de la conversation précédente. » + } + ] +) +``` + +### Document Integration and Search + +Easily manage and search through documents for your agents: + +```python +# Télécharger un document +document = client.agents.docs.create( +titre="Progrès de l'IA", +content="L'IA change le monde...", +métadonnées={"category": "article_de_recherche"} +) + +# Rechercher des documents +résultats = client.agents.docs.search( +texte="Progrès de l'IA", +metadata_filter={"category": "article_de_recherche"} +) +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Référence + +### Référence du SDK + +- **Node.js** [Référence SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) | [Package NPM](https://www.npmjs.com/package/@julep/sdk) +- **Python** [Référence SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) | [Package PyPI](https://pypi.org/project/julep/) + +### Référence API + +Explorez notre documentation API pour en savoir plus sur les agents, les tâches et les exécutions : + +- [API des agents](https://dev.julep.ai/api/docs#tag/agents) +- [API des tâches](https://dev.julep.ai/api/docs#tag/tasks) +- [API d'exécution](https://dev.julep.ai/api/docs#tag/executions) + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Démarrage rapide local + +**Exigences**: + +- dernier docker compose installé + +**Mesures**: + +1. `git clone https://github.com/julep-ai/julep.git` +2. `cd julep` +3. `docker volume create cozo_backup` +4. `docker volume create cozo_data` +5. `cp .env.example .env # <-- Modifier ce fichier` +6. `docker compose --env-file .env --profile temporal-ui --profile single-tenant --profile self-hosted-db up --build` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +--- + +## Quelle est la différence entre Julep et LangChain etc ? + +### Différents cas d'utilisation + +Considérez LangChain et Julep comme des outils avec des objectifs différents au sein de la pile de développement de l’IA. + +LangChain est idéal pour créer des séquences d'invites et gérer les interactions avec les LLM. Il dispose d'un vaste écosystème avec de nombreuses intégrations prédéfinies, ce qui le rend pratique si vous souhaitez mettre en place quelque chose rapidement. LangChain s'adapte bien aux cas d'utilisation simples qui impliquent une chaîne linéaire d'invites et d'appels d'API. + +Julep, en revanche, s'intéresse davantage à la création d'agents d'IA persistants capables de conserver le contexte lors d'interactions à long terme. Il est idéal lorsque vous avez besoin de flux de travail complexes impliquant des tâches en plusieurs étapes, une logique conditionnelle et une intégration avec divers outils ou API directement dans le processus de l'agent. Il est conçu dès le départ pour gérer les sessions persistantes et les flux de travail complexes. + +Utilisez Julep si vous imaginez créer un assistant IA complexe qui doit : + +- Suivez les interactions des utilisateurs sur plusieurs jours ou semaines. +- Exécutez des tâches planifiées, comme l'envoi de résumés quotidiens ou la surveillance de sources de données. +- Prendre des décisions basées sur des interactions antérieures ou des données stockées. +- Interagir avec plusieurs services externes dans le cadre de son flux de travail. + +Ensuite, Julep fournit l’infrastructure pour prendre en charge tout cela sans que vous ayez à le construire à partir de zéro. + +### Facteur de forme différent + +Julep est une **plateforme** qui comprend un langage pour décrire les workflows, un serveur pour exécuter ces workflows et un SDK pour interagir avec la plateforme. Pour créer quelque chose avec Julep, vous écrivez une description du workflow en YAML, puis vous exécutez le workflow dans le cloud. + +Julep est conçu pour les flux de travail lourds, en plusieurs étapes et de longue durée, et il n'y a aucune limite à la complexité du flux de travail. + +LangChain est une **bibliothèque** qui inclut quelques outils et un framework pour créer des chaînes linéaires d'invites et d'outils. Pour créer quelque chose avec LangChain, vous écrivez généralement du code Python qui configure et exécute les chaînes de modèles que vous souhaitez utiliser. + +LangChain pourrait être suffisant et plus rapide à mettre en œuvre pour les cas d'utilisation simples impliquant une chaîne linéaire d'invites et d'appels d'API. + +### En résumé + +Utilisez LangChain lorsque vous devez gérer les interactions LLM et les séquences d'invite dans un contexte sans état ou à court terme. + +Choisissez Julep lorsque vous avez besoin d'un framework robuste pour les agents avec état avec des capacités de workflow avancées, des sessions persistantes et une orchestration de tâches complexes. + +
+ + Back to Top +  |  + + Table of Contents + +
diff --git a/README-JA.md b/README-JA.md new file mode 100644 index 000000000..4ec52f579 --- /dev/null +++ b/README-JA.md @@ -0,0 +1,1680 @@ +[English](README.md) | [中文翻译](README-CN.md) | [日本語翻訳](README-JA.md) | [French](README-FR.md) + +
+ julep + julep +
+ +

+
+ ドキュメントを探索 (wip) + ドキュメントを探索 (wip) + · + 不和 + · + 𝕏 + · + リンクトイン +

+ +

+ NPM Version +   + PyPI - Version +   + Docker Image Version +   + GitHub License +

+ +--- + +> [!注意] +> API キーを [こちら](https://dashboard-dev.julep.ai) から取得します。 + +
+貢献🌟(クリックして拡大) + +## 貢献者募集🌟 + +Julep プロジェクトに新しい貢献者を迎えられることを嬉しく思います。プロジェクトを始めるのに役立つ「最初の良い問題」をいくつか作成しました。貢献する方法は次のとおりです。 + +1. 貢献方法に関するガイドラインについては、[CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/CONTRIBUTING.md) ファイルをご覧ください。 +2. [good first issues](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) を参照して、興味のあるタスクを見つけます。 +3. ご質問やご不明な点がございましたら、[Discord](https://discord.com/invite/JTSBGRZrzj) チャンネルまでお気軽にお問い合わせください。 + +あなたの貢献は、大小を問わず私たちにとって貴重です。一緒に素晴らしいものを作りましょう!🚀 + +
+ + + +

📖 目次

+ +- [貢献者募集🌟](#貢献者募集) +- [主な特徴](#主な特徴) +- [簡単な例](#簡単な例) +- [インストール](#インストール) +- [Python クイックスタート 🐍](#python-クイックスタート-) +- [Node.js Quick Start 🟩](#nodejs-quick-start-) +- [Components](#components) + - [Mental Model](#mental-model) +- [Concepts](#concepts) +- [Understanding Tasks](#understanding-tasks) + - [Lifecycle of a Task](#lifecycle-of-a-task) + - [Types of Workflow Steps](#types-of-workflow-steps) + - [Common Steps](#common-steps) + - [Key-Value Steps](#key-value-steps) + - [Iteration Steps](#iteration-steps) + - [Conditional Steps](#conditional-steps) + - [Other Control Flow](#other-control-flow) +- [Tool Types](#tool-types) + - [User-defined `functions`](#user-defined-functions) + - [`system` tools](#system-tools) + - [Available `system` resources and operations](#available-system-resources-and-operations) + - [Built-in `integrations`](#built-in-integrations) + - [Direct `api_calls`](#direct-api_calls) +- [Integrations](#integrations) +- [Other Features](#other-features) + - [Adding Tools to Agents](#adding-tools-to-agents) + - [Managing Sessions and Users](#managing-sessions-and-users) + - [Document Integration and Search](#document-integration-and-search) + - [SDK リファレンス](#sdk-リファレンス) + - [API リファレンス](#api-リファレンス) +- [ローカルクイックスタート](#ローカルクイックスタート) +- [Julep と LangChain などの違いは何ですか?](#julep-と-langchain-などの違いは何ですか) + - [さまざまなユースケース](#さまざまなユースケース) + - [異なるフォームファクタ](#異なるフォームファクタ) + + + +## 導入 + +Julep は、過去のやり取りを記憶し、複雑なタスクを実行できる AI エージェントを作成するためのプラットフォームです。長期記憶を提供し、複数ステップのプロセスを管理します。 + +Julep を使用すると、意思決定、ループ、並列処理、多数の外部ツールや API との統合を組み込んだ複数ステップのタスクを作成できます。 + +多くの AI アプリケーションは、最小限の分岐によるプロンプトと API 呼び出しの単純な線形チェーンに制限されていますが、Julep は次のようなより複雑なシナリオを処理できるように構築されています。 + +- 複数のステップがある、 +- モデルの出力に基づいて意思決定を行う +- 平行枝を生成し、 +- たくさんのツールを使い、 +- 長時間走る。 + +> [!ヒント] +> 単純な質問に答えるだけでなく、複雑なタスクを処理し、過去のやり取りを記憶し、場合によっては他のツールや API も使用できる AI エージェントを構築したいとします。そこで Julep の出番です。詳細については、[タスクの理解](#understanding-tasks) をお読みください。 + +## 主な特徴 + +1. 🧠 **永続的な AI エージェント**: 長期にわたるやり取りを通じてコン​​テキストと情報を記憶します。 +2. 💾 **ステートフル セッション**: 過去のやり取りを追跡して、パーソナライズされた応答を提供します。 +3. 🔄 **複数ステップのタスク**: ループと意思決定を含む複雑な複数ステップのプロセスを構築します。 +4. ⏳ **タスク管理**: 無期限に実行される可能性のある長時間実行タスクを処理します。 +5. 🛠️ **組み込みツール**: タスクで組み込みツールと外部 API を使用します。 +6. 🔧 **自己修復**: Julep は失敗したステップを自動的に再試行し、メッセージを再送信し、一般的にタスクがスムーズに実行されるようにします。 +6. 🔧 **自己修復**: Julep は失敗したステップを自動的に再試行し、メッセージを再送信し、一般的にタスクがスムーズに実行されるようにします。 +7. 📚 **RAG**: Julep のドキュメント ストアを使用して、独自のデータを取得して使用するためのシステムを構築します。 + +![機能](https://github.com/user-attachments/assets/4355cbae-fcbd-4510-ac0d-f8f77b73af70) + +> [!ヒント] +> Julep は、単純なプロンプト応答モデルを超えた AI ユースケースを必要とするアプリケーションに最適です。 + +## 簡単な例 + +次のことができる研究 AI エージェントを想像してください。 + +1. **トピックを選ぶ**、 +2. そのトピックについて**30個の検索クエリを考え出す** +3. ウェブ検索を並行して実行する +4. 結果を**要約**します。 +5. **要約を Discord に送信**します。 + +> [!注意] +> Julepでは、これは単一のタスクになります80行のコードそして走る完全に管理されたすべて自動的に行われます。すべての手順は Julep の独自のサーバー上で実行されるため、何もする必要はありません。 + +実際の例を次に示します。 + +```yaml +name: Research Agent + +# Optional: Define the input schema for the task +input_schema: + type: object + properties: + topic: + type: string + description: The main topic to research + num_questions: + type: integer + description: The number of search queries to generate + +# Define the tools that the agent can use +tools: + - name: web_search + type: integration + integration: + provider: brave + setup: + api_key: + + - name: discord_webhook + type: api_call + api_call: + url: https://discord.com/api/webhooks// + method: POST + headers: + Content-Type: application/json + +# Special variables: +# - inputs: for accessing the input to the task +# - outputs: for accessing the output of previous steps +# - _: for accessing the output of the previous step + +# Define the main workflow +main: +- prompt: + - role: system + content: >- + You are a research assistant. + Generate {{inputs[0].num_questions|default(30, true)}} diverse search queries related to the topic: + {{inputs[0].topic}} + + Write one query per line. + unwrap: true + +# Evaluate the search queries using a simple python expression +- evaluate: + search_queries: "_.split(NEWLINE)" + +# Run the web search in parallel for each query +- over: "_.search_queries" + map: + tool: web_search + arguments: + query: "_" + parallelism: 5 + +# Collect the results from the web search +- evaluate: + search_results: _ + +# Summarize the results +- prompt: + - role: system + content: > + You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. + The summary should be well-structured, informative, and highlight key findings and insights. Keep the summary concise and to the point. + The length of the summary should be less than 150 words. + Here are the search results: + {{_.search_results}} + unwrap: true + settings: + model: gpt-4o-mini + +- evaluate: + discord_message: |- + f''' + **Research Summary for {inputs[0].topic}** + {_} + ''' + + # Send the summary to Discord +- tool: discord_webhook + arguments: + json_: + content: _.discord_message[:2000] # Discord has a 2000 character limit +``` + +この例では、Julep は並列実行を自動的に管理し、失敗したステップを再試行し、API リクエストを再送信し、タスクが完了するまで確実に実行し続けます。 + +> これは 30 秒以内に実行され、次の出力を返します。 + +
+AIに関する研究概要 (クリックして拡大) + +> **AIに関する研究概要** +> +> ### 人工知能(AI)に関する研究成果の概要 +> +> #### はじめに +> +> 人工知能 (AI) の分野は近年、機械が環境を認識し、データから学習し、意思決定を行える方法とテクノロジーの開発により、大きな進歩を遂げています。この概要では、AI に関連するさまざまな研究結果から得られた洞察に主に焦点を当てています。 +> +> #### 主な調査結果 +> +> 1. **AIの定義と範囲**: +> +> - AI は、学習、推論、問題解決など、人間のような知能を必要とするタスクを実行できるシステムの作成に重点を置いたコンピューター サイエンスの分野として定義されています (Wikipedia)。 +> - 機械学習、自然言語処理、ロボット工学、コンピュータービジョンなど、さまざまなサブフィールドを網羅しています。 +> +> 2. **影響と応用**: +> +> - AI テクノロジーはさまざまな分野に統合され、効率性と生産性を向上させています。その応用範囲は、自律走行車やヘルスケア診断から顧客サービスの自動化や財務予測まで多岐にわたります (OpenAI)。 +> - AI をすべての人にとって有益なものにするという Google の取り組みは、さまざまなプラットフォームでユーザー エクスペリエンスを強化することで日常生活を大幅に改善する可能性を強調しています (Google AI)。 +> +> 3. **倫理的配慮**: +> +> - プライバシー、偏見、意思決定プロセスの説明責任に関する懸念など、AI の倫理的影響に関する議論が続いています。AI 技術の安全で責任ある使用を保証するフレームワークの必要性が強調されています (OpenAI)。 +> +> 4. **学習メカニズム**: +> +> - AI システムは、教師あり学習、教師なし学習、強化学習などのさまざまな学習メカニズムを活用します。これらの方法により、AI は過去の経験やデータから学習することで、時間の経過とともにパフォーマンスを向上させることができます (Wikipedia)。 +> - 教師あり学習と教師なし学習の区別は重要です。教師あり学習はラベル付きデータに依存しますが、教師なし学習は事前定義されたラベルなしでパターンを識別します (教師なし)。 +> +> 5. **今後の方向性**: +> - 今後の AI 開発では、AI システムの解釈可能性と透明性を高め、正当な判断と行動を提供できるようにすることに重点が置かれると予想されます (OpenAI)。 +> - AI システムをよりアクセスしやすく、ユーザーフレンドリーなものにし、さまざまな人口統計や業界での幅広い導入を促進する動きもあります (Google AI)。 +> +> #### 結論 +> +> AI は複数の領域に変革をもたらす力を持ち、産業の再構築や生活の質の向上が期待されています。しかし、AI の機能が拡大するにつれて、倫理的および社会的影響に対処することが極めて重要になります。AI の将来像を見据えるには、技術者、倫理学者、政策立案者による継続的な研究と協力が不可欠です。 + +
+ +## インストール + +Julep を使い始めるには、[npm](https://www.npmjs.com/package/@julep/sdk) または [pip](https://pypi.org/project/julep/) を使用してインストールします。 + +**Node.js**: + +```bash +npm install @julep/sdk + +# or + +bun add @julep/sdk +``` + +**Python**: + +```bash +pip install julep +``` + +> [!注意] +> API キーを [こちら](https://dashboard-dev.julep.ai) から取得します。 +> +> ベータ版では、[Discord](https://discord.com/invite/JTSBGRZrzj) に連絡して、API キーのレート制限を解除することもできます。 + +> [!ヒント] +> 💻 あなたは「コードを見せてください!™」タイプの人ですか? 始めるにあたって役立つクックブックを多数作成しました。**[クックブック](https://github.com/julep-ai/julep/tree/dev/cookbooks)** をチェックして、例を参照してください。 +> +> 💡 Julep をベースに構築できるアイデアもたくさんあります。**[アイデアのリスト](https://github.com/julep-ai/julep/tree/dev/cookbooks/IDEAS.md)** をチェックして、インスピレーションを得てください。 + +## Python クイックスタート 🐍 + +````python +### Step 0: Setup + +import time +import yaml +from julep import Julep # or AsyncJulep + +client = Julep(api_key="your_julep_api_key") + +### Step 1: Create an Agent + +agent = client.agents.create( + name="Storytelling Agent", + model="claude-3.5-sonnet", + about="You are a creative storyteller that crafts engaging stories on a myriad of topics.", +) + +### Step 2: Create a Task that generates a story and comic strip + +task_yaml = """ +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + type: integration + integration: + provider: wikipedia + method: search + +main: + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside ```応答の最後に yaml タグを追加します。 +アンラップ: true + +- 評価する: +plot_ideas: load_yaml(_.split('```yaml')[1].split('```')[0].ストリップ()) + +# ステップ2: プロットのアイデアから研究分野を抽出する +- プロンプト: +- 役割: システム +内容: あなたは {{agent.name}} です。 {{agent.about}} +- 役割: ユーザー +内容: > +ストーリーのプロットのアイデアをいくつか紹介します。 +{% for idea in _.plot_ideas %} +- {{アイデア}} +{% endfor %} + +ストーリーを展開するには、プロットのアイデアをリサーチする必要があります。 +何を研究すべきでしょうか? 興味深いと思うプロットのアイデアについて、Wikipedia の検索クエリを書き留めてください。 +出力をyamlリストとして返します```yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 + + - evaluate: + research_queries: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + + # Step 3: Research each plot idea + - foreach: + in: _.research_queries + do: + tool: research_wikipedia + arguments: + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + + # Step 4: Think and deliberate + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside ```応答の最後に yaml タグを追加します。yaml オブジェクトの構造は次のようになります。 + + ```yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""``` + +yaml が有効であり、文字とシーンが空でないことを確認してください。また、セミコロンや yaml の記述に関するその他の注意点にも注意してください。 +アンラップ: true + +- 評価する: +プロット: "load_yaml(_.split('```yaml')[1].split('```')[0].strip())" +""" + +タスク = client.tasks.create( +エージェントID=エージェントID、 +**yaml.safe_load(タスクyaml) +) + +### ステップ3: タスクを実行する + +実行 = client.executions.create( +タスクID=タスクID、 +input={"idea": "飛ぶことを学ぶ猫"} +) + +# 🎉 ストーリーと漫画パネルが生成される様子をご覧ください +while (result := client.executions.get(execution.id)).status が ['succeeded', 'failed'] の範囲外です: +print(結果.ステータス、結果.出力) +時間.睡眠(1) + +# 📦 実行が完了したら、結果を取得します +result.status == "成功"の場合: +print(結果.出力) +それ以外: +例外(結果.エラー)を発生させる +```` + +You can find the full python example [here](example.py). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Node.js Quick Start 🟩 + +```ジャバスクリプト +// ステップ 0: セットアップ +dotenv は、次のコードで定義されます。 +Julep の SDK を実装するには、次の手順に従ってください。 +yaml を require します。 + +config() を呼び出します。 + +constクライアント = 新しいジュレップ({ +APIキー: process.env.JULEP_API_KEY、 +環境: process.env.JULEP_ENVIRONMENT || "production", +}); + +/* ステップ 1: エージェントを作成する */ + +非同期関数createAgent() { +const エージェント = クライアント.エージェント.作成を待機します({ +名前: 「ストーリーテリングエージェント」 +モデル: "claude-3.5-sonnet", +について: +「あなたは、さまざまなトピックについて魅力的なストーリーを作り上げることができる創造的なストーリーテラーです。」 + }); +返品エージェント; +} + +/* ステップ 2: ストーリーと漫画を生成するタスクを作成する */ + +const タスクYaml = ` +名前: ストーリーテラー +説明: アイデアに基づいてストーリーを作成します。 + +ツール: +- 名前: research_wikipedia +統合: +提供元: wikipedia +方法: 検索 + +主要: +# ステップ1: プロットのアイデアを生み出す +- プロンプト: +- 役割: システム +内容: あなたは {{agent.name}} です。 {{agent.about}} +- 役割: ユーザー +内容: > +アイデア「{{_.idea}}」に基づいて、5 つのプロット アイデアのリストを生成します。自由に創造的に考えてください。出力は、応答の最後に \`\`\`yaml タグ内の長い文字列のリストとして返されます。 +アンラップ: true + +- 評価する: +plot_ideas: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# ステップ2: プロットのアイデアから研究分野を抽出する +- プロンプト: +- 役割: システム +内容: あなたは {{agent.name}} です。 {{agent.about}} +- 役割: ユーザー +内容: > +ストーリーのプロットのアイデアをいくつか紹介します。 +{% for idea in _.plot_ideas %} +- {{アイデア}} +{% endfor %} + +ストーリーを展開するには、プロットのアイデアをリサーチする必要があります。 +何を研究すべきでしょうか? 興味深いと思うプロットのアイデアについて、Wikipedia の検索クエリを書き留めてください。 +応答の最後に、\`\`\`yaml タグ内の yaml リストとして出力を返します。 +アンラップ: true +設定: +モデル: gpt-4o-mini +温度: 0.7 + +- 評価する: +リサーチクエリ: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + +# ステップ3: 各プロットのアイデアをリサーチする +- 各: +in: _.research_queries +する: +ツール: research_wikipedia +引数: +クエリ: _ + +- 評価する: +wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + +# ステップ4: 考えて熟考する +- プロンプト: +- 役割: システム +内容: あなたは {{agent.name}} です。 {{agent.about}} +- 役割: ユーザー +内容: |- +物語を書く前に、考え、熟考してみましょう。ここにいくつかのプロットのアイデアがあります: +{% 出力[1].plot_ideas のアイデア %} +- {{アイデア}} +{% endfor %} + +Wikipedia でプロットのアイデアを調査した結果は次のとおりです。 +{{_.wikipedia_results}} + +プロットのアイデアを批判的に考えます。プロットのアイデアと Wikipedia の結果を組み合わせて、ストーリーの詳細なプロットを作成します。 +メモや考えをすべて書き留めてください。 +最後に、レスポンスの最後にある \`\`\`yaml タグ内に yaml オブジェクトとしてプロットを記述します。yaml オブジェクトの構造は次のようになります。 + +\`\`\`yaml +タイトル: "" +文字: +- 名前: "" +について: "" +概要: "" +シーン: +- タイトル: "" +説明: "" +文字: +- 名前: "" +役割: "" +ストーリーライン: + - "「\`\`\` + +yaml が有効であり、文字とシーンが空でないことを確認してください。また、セミコロンや yaml の記述に関するその他の注意点にも注意してください。 +アンラップ: true + +- 評価する: +プロット: "load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip())" +`; + +非同期関数createTask(agentId) { +const タスク = client.tasks.create(agentId, yaml.parse(taskYaml)) を待機します。 +タスクを返す。 +} + +/* ステップ 3: タスクを実行する */ + +非同期関数executeTask(taskId) { +const 実行 = クライアント.実行.作成(taskId, { +const 実行 = クライアント.実行.作成(taskId, { +入力: { アイデア: 「飛ぶことを学ぶ猫」 }, + }); + +// 🎉 ストーリーと漫画パネルが生成される様子をご覧ください +(真)の間{ +const 結果 = client.executions.get(execution.id); を待機します。 +console.log(結果のステータス、結果の出力); + +if (result.status === "成功" || result.status === "失敗") { +// 📦 実行が終了したら、結果を取得します +if (result.status === "成功") { +console.log(結果の出力); +} それ以外 { +新しいエラーをスローします(result.error); + } +壊す; + } + +新しい Promise((resolve) => setTimeout(resolve, 1000)) を待機します。 + } +} + +// 例を実行するためのメイン関数 +非同期関数main() { +試す { +const エージェント = createAgent() を待機します。 +const タスク = createTask(agent.id); +タスクの実行を待機します(task.id); +} キャッチ(エラー){ +console.error("エラーが発生しました:", error); + } +} + +主要() +.then(() => console.log("完了")) +.catch(コンソール.エラー); +``` + +You can find the full Node.js example [here](example.js). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Components + +Julep is made up of the following components: + +- **Julep Platform**: The Julep platform is a cloud service that runs your workflows. It includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. +- **Julep SDKs**: Julep SDKs are a set of libraries for building workflows. There are SDKs for Python and JavaScript, with more on the way. +- **Julep API**: The Julep API is a RESTful API that you can use to interact with the Julep platform. + +### Mental Model + +
+ +
+ +Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's how to visualize it: + +1. **Your Application Code:** + + - You can use the Julep SDK in your application to define agents, tasks, and workflows. + - The SDK provides functions and classes that make it easy to set up and manage these components. + +2. **Julep Backend Service:** + + - The SDK communicates with the Julep backend over the network. + - The backend handles execution of tasks, maintains session state, stores documents, and orchestrates workflows. + +3. **Integration with Tools and APIs:** + - Within your workflows, you can integrate external tools and services. + - The backend facilitates these integrations, so your agents can, for example, perform web searches, access databases, or call third-party APIs. + +## Concepts + +Julep is built on several key technical components that work together to create powerful AI workflows: + +```マーメイド +グラフTD +ユーザー[ユーザー] ==> セッション[セッション] +セッション --> エージェント[エージェント] +エージェント --> タスク[タスク] +エージェント --> LLM[大規模言語モデル] +タスク --> ツール[ツール] +エージェント --> ドキュメント[ドキュメント] +ドキュメント --> VectorDB[ベクターデータベース] +タスク --> 実行[実行] + +classDef client fill:#9ff、stroke:#333、stroke-width:1px; +クラス User クライアント; + +classDef core fill:#f9f、stroke:#333、stroke-width:2px; +クラス Agent、Tasks、Session コア; +``` + +- **Agents**: AI-powered entities backed by large language models (LLMs) that execute tasks and interact with users. +- **Users**: Entities that interact with agents through sessions. +- **Sessions**: Stateful interactions between agents and users, maintaining context across multiple exchanges. +- **Tasks**: Multi-step, programmatic workflows that agents can execute, including various types of steps like prompts, tool calls, and conditional logic. +- **Tools**: Integrations that extend an agent's capabilities, including user-defined functions, system tools, or third-party API integrations. +- **Documents**: Text or data objects associated with agents or users, vectorized and stored for semantic search and retrieval. +- **Executions**: Instances of tasks that have been initiated with specific inputs, with their own lifecycle and state machine. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Understanding Tasks + +Tasks are the core of Julep's workflow system. They allow you to define complex, multi-step AI workflows that your agents can execute. Here's a brief overview of task components: + +- **Name, Description and Input Schema**: Each task has a unique name and description for easy identification. An input schema (optional) that is used to validate the input to the task. +- **Main Steps**: The core of a task, defining the sequence of actions to be performed. Each step can be a prompt, tool call, evaluate, wait_for_input, log, get, set, foreach, map_reduce, if-else, switch, sleep, or return. (See [Types of Workflow Steps](#types-of-workflow-steps) for more details) +- **Tools**: Optional integrations that extend the capabilities of your agent during task execution. + +### Lifecycle of a Task + +You create a task using the Julep SDK and specify the main steps that the agent will execute. When you execute a task, the following lifecycle happens: + +```マーメイド +シーケンス図 +参加者Dをあなたのコードとして +参加者C(ジュレップクライアント) +参加者Sはジュレップサーバーとして + +D->>C: タスクの作成 +C->>S: 実行を送信 +Sの上のメモ: タスクの実行 +Sに関する注意: 状態の管理 +S-->>C: 実行イベント +C-->>D: 進捗状況の更新 +S->>C: 実行完了 +C->>D: 最終結果 +``` + +### Types of Workflow Steps + +Tasks in Julep can include various types of steps, allowing you to create complex and powerful workflows. Here's an overview of the available step types: + +#### Common Steps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAboutSyntax
Prompt +Send a message to the AI model and receive a response +

Note: The prompt step uses Jinja templates and you can access context variables in them. +
+ +```ヤム +- プロンプト: 「次のデータを分析してください: {{agent.name}}」 # <-- これは jinja テンプレートです +``` + +```ヤム +- プロンプト: +- 役割: システム +内容: 「あなたは {{agent.name}} です。 {{agent.about}}」 +- 役割: ユーザー +内容: 「次のデータを分析します: {{_.data}}」 +``` + +
Tool Call +Execute an integrated tool or API that you have previously declared in the task. +

Note: The tool call step uses Python expressions inside the arguments. + +
+ +```ヤム +- ツール: web_search +引数: +クエリ: '"最新の AI 開発"' # <-- これは Python 式です (引用符に注意してください) +num_results: len(_.topics) # <-- リストの長さにアクセスするための Python 式 +``` + +
Evaluate +Perform calculations or manipulate data +

Note: The evaluate step uses Python expressions. +
+ +```ヤム +- 評価する: +average_score: 合計(スコア) / 長さ(スコア) +``` + +
Wait for Input +Pause workflow until input is received. It accepts an `info` field that can be used by your application to collect input from the user. + +

Note: The wait_for_input step is useful when you want to pause the workflow and wait for user input e.g. to collect a response to a prompt. + +
+ +```ヤム +- 入力待ち: +情報: +メッセージ: '"{_.required_info} に関する追加情報を提供してください。"' # <-- コンテキスト変数にアクセスするための Python 式 +``` + +
Log +Log a specified value or message. + +

Note: The log step uses Jinja templates and you can access context variables in them. + +
+ +```ヤム +- ログ: "アイテム {{_.item_id}} の処理が完了しました" # <-- コンテキスト変数にアクセスするための jinja テンプレート +``` + +
+ +#### Key-Value Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
Get +Retrieve a value from the execution's key-value store. + + + +```ヤム +- 取得: user_preference +``` + +
Set +Assign a value to a key in the execution's key-value store. + +

Note: The set step uses Python expressions. + +
+ +```ヤム +- セット: +user_preference: '"dark_mode"' # <-- python 式 +``` + +
+ +#### Iteration Steps + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Foreach +Iterate over a collection and perform steps for each item + + + +```ヤム +- 各: +in: _.data_list # <-- コンテキスト変数にアクセスするための Python 式 +する: +- ログ: "アイテム {{_.item}} を処理しています" # <-- コンテキスト変数にアクセスするための jinja テンプレート +``` + +
Map-Reduce +Map over a collection and reduce the results + + + +```ヤム +- マップリデュース: +over: _.numbers # <-- コンテキスト変数にアクセスするための Python 式 +地図: +- 評価する: +二乗: "_ ** 2" +Reduce: results + [_] # <-- (オプション) 結果を削減する Python 式。省略した場合、これがデフォルトになります。 +``` + +```ヤム +- マップリデュース: +以上: _.topics +地図: +- プロンプト: {{_}} に関するエッセイを書く +並列度: 10 +``` + +
Parallel +Run multiple steps in parallel + + + +```ヤム +- 平行: +- ツール: web_search +引数: +クエリ: 「AI ニュース」 +- ツール: weather_check +引数: +場所: '"ニューヨーク"' +``` + +
+ +#### Conditional Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
If-Else +Conditional execution of steps + + + +```ヤム +- if: _.score > 0.8 # <-- Python 式 +それから: +- ログ: 高得点を達成 +それ以外: +- エラー: スコアの改善が必要です +``` + +
Switch +Execute steps based on multiple conditions + + + +```ヤム +- スイッチ: +- ケース: _.category == 'A' +それから: +- ログ: 「カテゴリー A 処理」 +- ケース: _.category == 'B' +それから: +- ログ: 「カテゴリー B 処理」 +- case: _ # デフォルトのケース +それから: +- エラー: 不明なカテゴリ +``` + +
+ +#### Other Control Flow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Sleep +Pause the workflow for a specified duration + + + +```ヤム +- 寝る: +秒: 30 +分数: 1 +時間数: 1 +日数: 1 +``` + +
Return +Return a value from the workflow + +

Note: The return step uses Python expressions. + +
+ +```ヤム +- 戻る: +結果: '"タスクは正常に完了しました"' # <-- Python 式 +time: datetime.now().isoformat() # <-- python 式 +``` + +
Yield +Run a subworkflow and await its completion + + + +```ヤム +- 収率: +ワークフロー: process_data +引数: +input_data: _.raw_data # <-- Python式 +``` + +
Error +Handle errors by specifying an error message + + + +```ヤム +- エラー:「無効な入力が提供されています」# <-- 文字列のみ +``` + +
+ +Each step type serves a specific purpose in building sophisticated AI workflows. This categorization helps in understanding the various control flows and operations available in Julep tasks. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Tool Types + +Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a `web_search(query)` tool to search the Internet for some information. + +Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. + +Tools in julep can be one of: + +1. **User-defined `functions`**: These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. They need to be handled by the client. The workflow will pause until the client calls the function and gives the results back to julep. +2. **`system` tools**: Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. +3. **`integrations`**: Built-in third party tools that can be used to extend the capabilities of your agents. +4. **`api_calls`**: Direct api calls during workflow executions as tool calls. + +### User-defined `functions` + +These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + +```ヤム +名前: システムツールタスクの例 +説明: システムコールを使用してエージェントを一覧表示する + +ツール: +- 名前: send_notification +説明: ユーザーに通知を送信する +タイプ: 関数 +関数: +パラメータ: +タイプ: オブジェクト +プロパティ: +文章: +タイプ: 文字列 +説明: 通知の内容 + +主要: +- ツール: send_notification +引数: +内容: '"hi"' # <-- Python 式 +``` + +Whenever julep encounters a _user-defined function_, it pauses, giving control back to the client and waits for the client to run the function call and give the results back to julep. + +### `system` tools + +Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + +`system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + +For example, + +```ヤム +名前: システムツールタスクの例 +説明: システムコールを使用してエージェントを一覧表示する + +ツール: +- 名前: list_agent_docs +説明: 指定されたエージェントのすべてのドキュメントを一覧表示します +タイプ: システム +システム: +リソース: エージェント +サブリソース: doc +操作: リスト + +主要: +- ツール: list_agents +引数: +制限: 10 # <-- Python式 +``` + +#### Available `system` resources and operations + +- `agent`: + + - `list`: List all agents. + - `get`: Get a single agent by id. + - `create`: Create a new agent. + - `update`: Update an existing agent. + - `delete`: Delete an existing agent. + +- `user`: + + - `list`: List all users. + - `get`: Get a single user by id. + - `create`: Create a new user. + - `update`: Update an existing user. + - `delete`: Delete an existing user. + +- `session`: + + - `list`: List all sessions. + - `get`: Get a single session by id. + - `create`: Create a new session. + - `update`: Update an existing session. + - `delete`: Delete an existing session. + - `chat`: Chat with a session. + - `history`: Get the chat history with a session. + +- `task`: + + - `list`: List all tasks. + - `get`: Get a single task by id. + - `create`: Create a new task. + - `update`: Update an existing task. + - `delete`: Delete an existing task. + +- `doc` (subresource for `agent` and `user`): + - `list`: List all documents. + - `create`: Create a new document. + - `delete`: Delete an existing document. + - `search`: Search for documents. + +Additional operations available for some resources: + +- `embed`: Embed a resource (specific resources not specified in the provided code). +- `change_status`: Change the status of a resource (specific resources not specified in the provided code). +- `chat`: Chat with a resource (specific resources not specified in the provided code). +- `history`: Get the chat history with a resource (specific resources not specified in the provided code). +- `create_or_update`: Create a new resource or update an existing one (specific resources not specified in the provided code). + +Note: The availability of these operations may vary depending on the specific resource and implementation details. + +> [!TIP] > **Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +### Built-in `integrations` + +Julep comes with a number of built-in integrations (as described in the section below). `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + +See [Integrations](#integrations) for details on the available integrations. + +> [!TIP] > **Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +### Direct `api_calls` + +julep can also directly make api calls during workflow executions as tool calls. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + +For example, + +```ヤム +名前: api_callタスクの例 +ツール: +- タイプ: api_call +名前: こんにちは +API呼び出し: +メソッド: GET +URL: https://httpbin.org/get + +主要: +- ツール: こんにちは +引数: +書式: +test: _.input # <-- Python式 +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Integrations + +Julep supports various integrations that extend the capabilities of your AI agents. Here's a list of available integrations and their supported arguments: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Brave Search + +```ヤム +設定: +api_key: 文字列 # Brave SearchのAPIキー + +引数: +query: 文字列 # Braveで検索するための検索クエリ + +出力: +result: 文字列 # Brave Searchの結果 +``` + + + +**Example cookbook**: [cookbooks/02-sarcastic-news-headline-generator.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/02-sarcastic-news-headline-generator.ipynb) + +
BrowserBase + +```ヤム +設定: +api_key: 文字列 # BrowserBaseのAPIキー +project_id: 文字列 # BrowserBase のプロジェクト ID +session_id: 文字列 # (オプション) BrowserBaseのセッションID + +引数: +urls: list[string] # BrowserBaseで読み込むURL + +出力: +documents: list # URLから読み込まれたドキュメント +``` + + + +**Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +
Email + +```ヤム +設定: +ホスト: 文字列 # メールサーバーのホスト +port: 整数 # メールサーバーのポート +user: 文字列 # メールサーバーのユーザー名 +パスワード: 文字列 # メールサーバーのパスワード + +引数: +to: 文字列 # メールを送信するメールアドレス +from: 文字列 # メールを送信するメールアドレス +subject: 文字列 # メールの件名 +body: 文字列 # メールの本文 + +出力: +success: boolean # メールが正常に送信されたかどうか +``` + + + +**Example cookbook**: [cookbooks/00-Devfest-Email-Assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/00-Devfest-Email-Assistant.ipynb) + +
Spider + +```ヤム +設定: +spider_api_key: 文字列 # SpiderのAPIキー + +引数: +url: 文字列 # データを取得するURL +mode: 文字列 # クローラーのタイプ (デフォルト: "scrape") +params: dict # (オプション) Spider APIのパラメータ + +出力: +ドキュメント: リスト # スパイダーから返されたドキュメント +``` + + + +**Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +
Weather + +```ヤム +設定: +openweathermap_api_key: 文字列 # OpenWeatherMapのAPIキー + +引数: +location: 文字列 # 気象データを取得する場所 + +出力: +結果: 文字列 # 指定された場所の天気データ +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
Wikipedia + +```ヤム +引数: +query: 文字列 # 検索クエリ文字列 +load_max_docs: 整数 # (オプション) ロードするドキュメントの最大数。デフォルトは 2 です。 + +出力: +ドキュメント: リスト # Wikipedia 検索から返されたドキュメント +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
FFmpeg + +```ヤム +引数: +cmd: 文字列 # 実行するFFmpegコマンド +file: 文字列 # 処理するbase64エンコードされたファイル + +出力: +fileoutput: 文字列 # FFmpeg コマンドからの出力ファイル(base64 エンコード) +結果: ブール値 # FFmpeg コマンドが正常に実行されたかどうか +mime_type: 文字列 # 出力ファイルのMIMEタイプ +``` + +
Llama Parse + +```ヤム +設定: +llamaparse_api_key: string # Llama Parse の API キー +params: dict # (オプション) Llama Parse 統合の追加パラメータ + +引数: +ファイル: 文字列 | 配列# 解析する base64 でエンコードされたファイル、または読み込む http/https URL の配列。 +filename: 文字列 # (オプション)。ファイルのファイル名。デフォルトはランダムな UUID です。ファイルが base64 でエンコードされた文字列の場合にのみ使用されます。 +params: dict # (オプション) Llama Parse 統合の追加パラメータ。セットアップパラメータを上書きします。 +base64: boolean # 入力ファイルが base64 でエンコードされているかどうか。デフォルトは false です。 + +出力: +ドキュメント: リスト # ドキュメントから解析されたデータ +``` + +
Cloudinary + +```ヤム + +method: media_upload | media_edit # Cloudinary統合に使用するメソッド + +設定: +cloudinary_cloud_name: 文字列 # Cloudinary クラウド名 +cloudinary_api_key: 文字列 # Cloudinary API キー +cloudinary_api_secret: 文字列 # Cloudinary API シークレット +params: dict # (オプション) Cloudinary統合の追加パラメータ + +引数: +file: 文字列 # ファイルのアップロード先の URL。media_upload メソッドでのみ使用できます。 +upload_params: dict # (オプション) アップロード用の追加パラメータ。media_upload メソッドでのみ使用できます。 +public_id: 文字列 # (オプション) ファイルのパブリック ID。media_edit メソッドの場合は必須です。media_upload メソッドの場合はオプションです。デフォルトはランダムな UUID です。 +transformation: list[dict] # ファイルに適用する変換。media_edit メソッドでのみ使用できます。 +return_base64: boolean # ファイルを base64 エンコードで返すかどうか。デフォルトは false です。 + +出力: +url: 文字列 # アップロードされたファイルの URL。media_upload メソッドでのみ使用できます。 +meta_data: dict # アップロード応答からの追加メタデータ。media_upload メソッドでのみ使用できます。 +public_id: 文字列 # アップロードされたファイルのパブリック ID。media_upload メソッドでのみ使用できます。 +transformed_url: 文字列 # (オプション) 変換された URL。media_edit メソッドでのみ使用できます。 +base64: 文字列 # (オプション) return_base64 が true の場合、base64 でエンコードされたファイル。 +``` + + + +**Example cookbook**: [cookbooks/05-video-processing-with-natural-language.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/05-video-processing-with-natural-language.ipynb) + +
+ +For more details, refer to our [Integrations Documentation](#integrations). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Other Features + +Julep offers a range of advanced features to enhance your AI workflows: + +### Adding Tools to Agents + +Extend your agent's capabilities by integrating external tools and APIs: + +```パイソン +クライアント.エージェント.ツール.作成( +エージェントID=エージェントID、 +名前="ウェブ検索", +description="Web で情報を検索します。", +統合={ +「プロバイダー」:「勇敢な」、 +"メソッド": "検索", +"セットアップ": {"api_key": "your_brave_api_key"}, + }, +) +``` + +### Managing Sessions and Users + +Julep provides robust session management for persistent interactions: + +```パイソン +セッション = client.sessions.create( +エージェントID=エージェントID、 +user_id=ユーザーID、 +context_overflow="適応型" +) + +# 同じセッションで会話を続ける +レスポンス = client.sessions.chat( +session_id=セッションID、 +session_id=セッションID、 +メッセージ=[ + { +「役割」: 「ユーザー」、 +"content": "前回の会話をフォローアップします。" + } + ] +) +``` + +### Document Integration and Search + +Easily manage and search through documents for your agents: + +```パイソン +# ドキュメントをアップロードする +ドキュメント = client.agents.docs.create( +title="AIの進歩", +content="AI は世界を変えています...", +メタデータ = {"カテゴリ": "研究論文"} +) + +# ドキュメントを検索 +結果 = client.agents.docs.search( +text="AIの進歩", +metadata_filter={"category": "研究論文"} +) +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## 参照 + +### SDK リファレンス + +- **Node.js** [SDK リファレンス](https://github.com/julep-ai/node-sdk/blob/main/api.md) | [NPM パッケージ](https://www.npmjs.com/package/@julep/sdk) +- **Python** [SDK リファレンス](https://github.com/julep-ai/python-sdk/blob/main/api.md) | [PyPI パッケージ](https://pypi.org/project/julep/) + +### API リファレンス + +エージェント、タスク、実行の詳細については、API ドキュメントをご覧ください。 + +- [エージェント API](https://dev.julep.ai/api/docs#tag/agents) +- [タスク API](https://dev.julep.ai/api/docs#tag/tasks) +- [実行API](https://dev.julep.ai/api/docs#tag/executions) + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## ローカルクイックスタート + +**要件**: + +- 最新の docker compose がインストールされている + +**手順**: + +1. `git clone https://github.com/julep-ai/julep.git` +2. `cd ジュレップ` +3. `docker volume create cozo_backup` +4. docker ボリュームを作成します cozo_data +4. docker ボリュームを作成します cozo_data +5. `cp .env.example .env # <-- このファイルを編集します` +6. `docker compose --env-file .env --profile temporal-ui --profile single-tenant --profile self-hosted-db up --build` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +--- + +## Julep と LangChain などの違いは何ですか? + +### さまざまなユースケース + +LangChain と Julep は、AI 開発スタック内で異なる重点を置いたツールと考えてください。 + +LangChain は、プロンプトのシーケンスを作成し、LLM とのやり取りを管理するのに最適です。多数の事前構築された統合を備えた大規模なエコシステムを備えているため、何かをすぐに起動して実行したい場合に便利です。LangChain は、プロンプトと API 呼び出しの線形チェーンを含む単純なユースケースに適しています。 + +一方、Julep は、長期的なインタラクションでコンテキストを維持できる永続的な AI エージェントの構築に重点を置いています。複数ステップのタスク、条件付きロジック、エージェントのプロセス内で直接さまざまなツールや API との統合を伴う複雑なワークフローが必要な場合に効果を発揮します。永続的なセッションと複雑なワークフローを管理するために、ゼロから設計されています。 + +以下のことが必要となる複雑な AI アシスタントの構築を考えている場合には、Julep を使用してください。 + +- 数日または数週間にわたってユーザーのインタラクションを追跡します。 +- 毎日のサマリーの送信やデータ ソースの監視などのスケジュールされたタスクを実行します。 +- 以前のやり取りや保存されたデータに基づいて決定を下します。 +- ワークフローの一部として複数の外部サービスと対話します。 + +そして、Julep は、ゼロから構築する必要なく、これらすべてをサポートするインフラストラクチャを提供します。 + +### 異なるフォームファクタ + +Julep は、ワークフローを記述するための言語、それらのワークフローを実行するためのサーバー、およびプラットフォームと対話するための SDK を含む **プラットフォーム** です。Julep で何かを構築するには、ワークフローの説明を `YAML` で記述し、クラウドでワークフローを実行します。 + +Julep は、負荷の高い、複数のステップから成る、長時間実行されるワークフロー向けに構築されており、ワークフローの複雑さに制限はありません。 + +LangChain は、プロンプトとツールの線形チェーンを構築するためのいくつかのツールとフレームワークを含む **ライブラリ** です。LangChain を使用して何かを構築するには、通常、使用するモデル チェーンを設定して実行する Python コードを記述します。 + +LangChain は、プロンプトと API 呼び出しの線形チェーンを含む単純なユースケースでは十分であり、実装も迅速です。 + +### 要約すれば + +ステートレスまたは短期的なコンテキストで LLM インタラクションとプロンプト シーケンスを管理する必要がある場合は、LangChain を使用します。 + +高度なワークフロー機能、永続的なセッション、複雑なタスク オーケストレーションを備えたステートフル エージェント用の堅牢なフレームワークが必要な場合は、Julep を選択してください。 + +
+ + Back to Top +  |  + + Table of Contents + +
diff --git a/README-JP.md b/README-JP.md deleted file mode 100644 index 8cd716ade..000000000 --- a/README-JP.md +++ /dev/null @@ -1,689 +0,0 @@ -[English](README.md) | [中文翻译](/README-CN.md) | 日本語 - -
- julep -
- -

-
- ドキュメントを探索する - · - Discord - · - 𝕏 - · - LinkedIn -

- - -

- NPM Version -   - PyPI - Version -   - Docker Image Version -   - GitHub License -

- -***** - -> [!TIP] -> 👨‍💻 devfest.aiイベントに参加されましたか?私たちの[Discord](https://discord.com/invite/JTSBGRZrzj)に参加して、以下の詳細をご確認ください。 - -
-🌟 コントリビューターとDevFest.AI参加者の皆様へ: - -## 🌟 コントリビューター募集! - -Julepプロジェクトに新しいコントリビューターを歓迎します!スタートに役立つ「初心者向けの問題」をいくつか作成しました。以下は、貢献する方法です: - -1. [CONTRIBUTING.md](CONTRIBUTING.md)ファイルを確認して、貢献のガイドラインを確認してください。 -2. [初心者向けの問題](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)を閲覧して、興味のあるタスクを見つけてください。 -3. 質問がある場合や助けが必要な場合は、[Discord](https://discord.com/invite/JTSBGRZrzj)チャンネルでお気軽にお問い合わせください。 - -あなたの貢献、大きいものでも小さいものでも、私たちにとっては貴重です。一緒に素晴らしいものを作りましょう!🚀 - -### 🎉 DevFest.AI 2024年10月 - -エキサイティングなニュース!2024年10月中、DevFest.AIに参加します!🗓️ - -- このイベント中にJulepに貢献し、素晴らしいJulepのグッズや景品を獲得するチャンスを得ましょう!🎁 -- 世界中の開発者と一緒にAIリポジトリに貢献し、素晴らしいイベントに参加しましょう。 -- この素晴らしいイニシアチブを組織してくれたDevFest.AIに大きな感謝を! - -> [!TIP] -> 楽しみに参加する準備はできましたか?**[ツイートして参加を開始](https://twitter.com/intent/tweet?text=Pumped%20to%20be%20participating%20in%20%40devfestai%20with%20%40julep_ai%20building%20%23ai%20%23agents%20%23workflows%20Let's%20gooo!%20https%3A%2F%2Fgit.new%2Fjulep)**し、コーディングを始めましょう!🖥️ - -![Julep DevFest.AI](https://media.giphy.com/media/YjyUeyotft6epaMHtU/giphy.gif) - -
- - - -
-

📖 Table of Contents

- -- [紹介](#%E7%B4%B9%E4%BB%8B) -- [特徴](#%E7%89%B9%E5%BE%B4) -- [インストール](#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB) -- [クイックスタートガイド](#%E3%82%AF%E3%82%A4%E3%83%83%E3%82%AF%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%82%AC%E3%82%A4%E3%83%89) - - [ステップ1:Julepをインポートする](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%971julep%E3%82%92%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E3%81%99%E3%82%8B) - - [ステップ2:エージェントを初期化する](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%972%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%82%92%E5%88%9D%E6%9C%9F%E5%8C%96%E3%81%99%E3%82%8B) - - [ステップ3:エージェントとチャットする](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%973%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%81%A8%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E3%81%99%E3%82%8B) - - [ステップ4:多段階タスクを作成する](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%974%E5%A4%9A%E6%AE%B5%E9%9A%8E%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B) - - [ステップ5:タスクを実行する](#%E3%82%B9%E3%83%86%E3%83%83%E3%83%975%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B) -- [概念](#%E6%A6%82%E5%BF%B5) - - [エージェント](#%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88) - - [ユーザー](#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC) - - [セッション](#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3) - - [タスク](#%E3%82%BF%E3%82%B9%E3%82%AF) - - [ツール](#%E3%83%84%E3%83%BC%E3%83%AB) - - [ドキュメント](#%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88) - - [実行](#%E5%AE%9F%E8%A1%8C) -- [タスクの理解](#%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E7%90%86%E8%A7%A3) - - [ワークフローステップの種類](#%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%81%AE%E7%A8%AE%E9%A1%9E) -- [高度な機能](#%E9%AB%98%E5%BA%A6%E3%81%AA%E6%A9%9F%E8%83%BD) - - [エージェントにツールを追加する](#%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%81%AB%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B) - - [セッションとユーザーの管理](#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AE%E7%AE%A1%E7%90%86) - - [ドキュメントの統合と検索](#%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E7%B5%B1%E5%90%88%E3%81%A8%E6%A4%9C%E7%B4%A2) -- [SDKリファレンス](#sdk%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9) -- [APIリファレンス](#api%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9) -- [例とチュートリアル](#%E4%BE%8B%E3%81%A8%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB) -- [貢献](#%E8%B2%A2%E7%8C%AE) -- [サポートとコミュニティ](#%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%83%86%E3%82%A3) -- [ライセンス](#%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9) -- [謝辞](#%E8%AC%9D%E8%BE%9E) - -
- - -## 紹介 - -Julepは、カスタマイズ可能なワークフローを持つ持続可能なAIエージェントを作成するためのオープンソースプラットフォームです。柔軟性と使いやすさに重点を置いて、AI駆動のアプリケーションを開発、管理、展開するためのツールを提供します。 - -Julepを使用すると、次のことができます: -- 複数のインタラクションにわたってコンテキストと状態を保持するAIエージェントを迅速に開発する -- AIエージェントに合わせた洗練されたワークフローを設計および実行する -- さまざまなツールやAPIをAIワークフローにシームレスに統合する -- 持続的なセッションとユーザーインタラクションを簡単に管理する - -チャットボットの開発、タスクの自動化、または複雑なAIアシスタントの構築を行う場合でも、Julepはアイデアを迅速かつ効率的に現実に変えるために必要な柔軟性と機能を提供します。 - - - -
-ここに簡単なPythonの例があります: - - - -

-from julep import Julep, AsyncJulep
-
-# 🔑 Julepクライアントを初期化する
-#     または、非同期操作のためにAsyncJulepを使用する
-client = Julep(api_key="your_api_key")
-
-##################
-## 🤖 エージェント 🤖 ##
-##################
-
-# 研究エージェントを作成する
-agent = client.agents.create(
-    name="Research Agent",
-    model="claude-3.5-sonnet",
-    about="You are a research agent designed to handle research inquiries.",
-)
-
-# 🔍 エージェントにウェブ検索ツールを追加する
-client.agents.tools.create(
-    agent_id=agent.id,
-    name="web_search",  # Pythonの有効な変数名である必要があります
-    description="Use this tool to research inquiries.",
-    integration={
-        "provider": "brave",
-        "method": "search",
-        "setup": {
-            "api_key": "your_brave_api_key",
-        },
-    },
-)
-
-#################
-## 💬 チャット 💬 ##
-#################
-
-# エージェントとのインタラクティブなチャットセッションを開始する
-session = client.sessions.create(
-    agent_id=agent.id,
-    context_overflow="adaptive",  # 🧠 必要に応じてJulepがコンテキストウィンドウを動的に計算します
-)
-
-# 🔄 チャットループ
-while (user_input := input("You: ")) != "exit":
-    response = client.sessions.chat(
-        session_id=session.id,
-        message=user_input,
-    )
-
-    print("Agent: ", response.choices[0].message.content)
-
-
-#################
-## 📋 タスク 📋 ##
-#################
-
-# エージェントのための定期的な研究タスクを作成する
-task = client.tasks.create(
-    agent_id=agent.id,
-    name="Research Task",
-    description="Research the given topic every 24 hours.",
-    #
-    # 🛠️ タスク固有のツール
-    tools=[
-        {
-            "name": "send_email",
-            "description": "Send an email to the user with the results.",
-            "api_call": {
-                "method": "post",
-                "url": "https://api.sendgrid.com/v3/mail/send",
-                "headers": {"Authorization": "Bearer YOUR_SENDGRID_API_KEY"},
-            },
-        }
-    ],
-    #
-    # 🔢 タスクの主なステップ
-    main=[
-        #
-        # ステップ1:トピックを調査する
-        {
-            # `_`(アンダースコア)変数は前のステップの出力を指します
-            # ここでは、ユーザーからのトピック入力を指します
-            "prompt": "Look up topic '{{_.topic}}' and summarize the results.",
-            "tools": [{"ref": {"name": "web_search"}}],  # 🔍 エージェントのウェブ検索ツールを使用する
-            "unwrap": True,
-        },
-        #
-        # ステップ2:研究結果を含むメールを送信する
-        {
-            "tool": "send_email",
-            "arguments": {
-                "subject": "Research Results",
-                "body": "'Here are the research results for today: ' + _.content",
-                "to": "inputs[0].email",  # ユーザーの入力からメールを参照する
-            },
-        },
-        #
-        # ステップ3:繰り返す前に24時間待つ
-        {"sleep": "24 * 60 * 60"},
-    ],
-)
-
-# 🚀 定期的なタスクを開始する
-client.executions.create(task_id=task.id, input={"topic": "Python"})
-
-# 🔁 これにより、タスクは24時間ごとに実行され、
-#    "Python"のトピックを調査し、
-#    結果をユーザーのメールに送信します
-
-
- - -## 特徴 - -Julepは、カスタマイズ可能なワークフローを持つ持続可能なAIエージェントの構築プロセスを簡素化します。主な特徴は次のとおりです: - -- **持続可能なAIエージェント**:複数のインタラクションにわたってコンテキストを保持するAIエージェントを作成および管理します。 -- **カスタマイズ可能なワークフロー**:タスクを使用して複雑な多段階のAIワークフローを設計します。 -- **ツール統合**:さまざまなツールやAPIをAIワークフローにシームレスに統合します。 -- **ドキュメント管理**:エージェントのためのドキュメントを効率的に管理および検索します。 -- **セッション管理**:継続的なインタラクションのための持続的なセッションを処理します。 -- **柔軟な実行**:ワークフローでの並行処理、条件ロジック、およびエラー処理をサポートします。 - -## インストール - -Julepを始めるには、[npm](https://www.npmjs.com/package/@julep/sdk)または[pip](https://pypi.org/project/julep/)を使用してインストールします: - -```bash -npm install @julep/sdk -``` - -または - -```bash -pip install julep -``` - -> [!TIP] -> ~~APIキーを[こちら](https://app.julep.ai/api-keys)から取得してください。~~ -> -> ベータ版の間、APIキーを取得するには[Discord](https://discord.com/invite/JTSBGRZrzj)でお問い合わせください。 - -## クイックスタートガイド - -### ステップ1:Julepをインポートする - -まず、Julep SDKをプロジェクトにインポートします: - -```javascript -const Julep = require('@julep/sdk'); -``` - -または - -```python -from julep import AsyncJulep -``` - -### ステップ2:エージェントを初期化する - -基本設定で新しいエージェントを作成します: - -```javascript -const julep = new Julep({ apiKey: 'your-api-key' }); - -const agent = await julep.agents.create({ - name: 'ResearchAssistant', - model: 'gpt-4-turbo', - about: "You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", -}); -``` - -または - -```python -client = AsyncJulep(api_key="your_api_key") - -agent = await client.agents.create( - name="Storytelling Agent", - model="gpt-4-turbo", - about="You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", -) -``` - -### ステップ3:エージェントとチャットする - -エージェントとのインタラクティブなチャットセッションを開始します: - -```javascript -const session = await julep.sessions.create({ - agentId: agent.id, -}); - -// エージェントにメッセージを送信する -const response = await julep.sessions.chat({ - sessionId: session.id, - message: 'Hello, can you tell me a story?', -}); - -console.log(response); -``` - -または - -```python -session = await client.sessions.create(agent_id=agent.id) - -# エージェントにメッセージを送信する -response = await client.sessions.chat( - session_id=session.id, - message="Hello, can you tell me a story?", -) - -print(response) -``` - - -### ステップ4:多段階タスクを作成する - -入力されたアイデアに基づいてストーリーを作成し、パネル化されたコミックストリップを生成する多段階タスクを定義しましょう: - -```python -# 🛠️ エージェントに画像生成ツール(DALL·E)を追加する -await client.agents.tools.create( - agent_id=agent.id, - name="image_generator", - description="Use this tool to generate images based on descriptions.", - integration={ - "provider": "dalle", - "method": "generate_image", - "setup": { - "api_key": "your_dalle_api_key", - }, - }, -) - -# 📋 タスク -# アイデアを受け取り、ストーリーと4コマ漫画を作成するタスクを作成する -task = await client.tasks.create( - agent_id=agent.id, - name="Story and Comic Creator", - description="Create a story based on an idea and generate a 4-panel comic strip illustrating the story.", - main=[ - # ステップ1:ストーリーを生成し、4つのパネルに要約する - { - "prompt": [ - { - "role": "system", - "content": "You are {{agent.name}}. {{agent.about}}" - }, - { - "role": "user", - "content": ( - "Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. " - "Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story." - ), - }, - ], - "unwrap": True, - }, - # ステップ2:パネルの説明とストーリーを抽出する - { - "evaluate": { - "story": "_.split('1. ')[0].strip()", - "panels": "re.findall(r'\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)', _)", - } - }, - # ステップ3:画像生成ツールを使用して各パネルの画像を生成する - { - "foreach": { - "in": "_.panels", - "do": { - "tool": "image_generator", - "arguments": { - "description": "_", - }, - }, - }, - }, - # ステップ4:ストーリーのキャッチーなタイトルを生成する - { - "prompt": [ - { - "role": "system", - "content": "You are {{agent.name}}. {{agent.about}}" - }, - { - "role": "user", - "content": "Based on the story below, generate a catchy title.\n\nStory: {{outputs[1].story}}", - }, - ], - "unwrap": True, - }, - # ステップ5:ストーリー、生成された画像、およびタイトルを返す - { - "return": { - "title": "outputs[3]", - "story": "outputs[1].story", - "comic_panels": "[output.image.url for output in outputs[2]]", - } - }, - ], -) -``` - -> [!TIP] -> これのnode.jsバージョンは似ています。 - -### ステップ5:タスクを実行する - -```python -# 🚀 アイデアを入力してタスクを実行する -execution = await client.executions.create( - task_id=task.id, - input={"idea": "A cat who learns to fly"} -) - -# 🎉 ストーリーとコミックパネルが生成される様子を見守る -await client.executions.stream(execution_id=execution.id) -``` - -この例は、カスタムツールを持つエージェントを作成し、複数のステップを持つ複雑なタスクを定義し、それを実行してクリエイティブな出力を生成する方法を示しています。 - - - -> [!TIP] -> もう一つのnode.jsの例は[こちら](example.ts)またはpythonの例は[こちら](example.py)にあります。 - -## 概念 - -Julepは、強力なAIワークフローを作成するために連携するいくつかの主要な技術コンポーネントに基づいて構築されています: - -### エージェント -タスクを実行し、ユーザーと対話する大規模な言語モデル(LLM)に支えられたAIエンティティ。エージェントはJulepのコア機能ユニットです。 - -```mermaid -graph TD - Agent[Agent] --> LLM[Large Language Model] - Agent --> Tasks[Tasks] - Agent --> Users[Users] - Tasks --> Tools[Tools] -``` - -### ユーザー -エージェントと対話するエンティティ。ユーザーはセッションに関連付けられ、独自のメタデータを持つことができ、個別の対話が可能になります。 - -```mermaid -graph LR - User[User] --> Sessions[Sessions] - Sessions --> Agents[Agents] - Sessions --> Metadata[Metadata] -``` - -### セッション -エージェントとユーザーの間の有状態の対話。セッションは複数の交換にわたってコンテキストを保持し、コンテキスト管理やオーバーフロー処理などの異なる動作に対して構成できます。 - -```mermaid -graph LR - Sessions[Sessions] --> Agents[Agents] - Sessions --> Users[Users] - Sessions --> ContextManagement[Context Management] - Sessions --> OverflowHandling[Overflow Handling] -``` - -### タスク -エージェントが実行できる多段階のプログラムワークフロー。タスクは複雑な操作を定義し、プロンプト、ツール呼び出し、条件ロジックなどのさまざまなタイプのステップを含むことができます。 - -```mermaid -graph TD - Tasks[Tasks] --> Steps[Workflow Steps] - Steps --> Prompt[Prompt] - Steps --> ToolCalls[Tool Calls] - Steps --> ConditionalLogic[Conditional Logic] -``` - -### ツール -エージェントの能力を拡張する統合。ツールはユーザー定義の関数、システムツール、またはサードパーティのAPI統合である可能性があります。これにより、エージェントはテキスト生成を超えたアクションを実行できます。 - -```mermaid -graph LR - Tools[Tools] --> UserDefinedFunctions[User-Defined Functions] - Tools --> SystemTools[System Tools] - Tools --> ThirdPartyAPIs[Third-Party APIs] -``` - -### ドキュメント -エージェントまたはユーザーに関連付けることができるテキストまたはデータオブジェクト。ドキュメントはベクトル化され、エージェントの対話中にセマンティック検索と取得を可能にするベクトルデータベースに保存されます。 - -```mermaid -graph LR - Documents[Documents] --> VectorDatabase[Vector Database] - Documents --> SemanticSearch[Semantic Search] - Documents --> AgentsOrUsers[Agents or Users] -``` - -### 実行 -特定の入力で開始されたタスクのインスタンス。実行には独自のライフサイクルと状態マシンがあり、長時間実行されるプロセスの監視、管理、および再開が可能です。 - -```mermaid -graph LR - Executions[Executions] --> Tasks[Tasks] - Executions --> Lifecycle[Lifecycle] - Executions --> Monitoring[Monitoring] - Executions --> Management[Management] - Executions --> Resumption[Resumption] -``` - -これらの概念とその相互作用の詳細な説明については、[概念ドキュメント](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)を参照してください。 - -## タスクの理解 - -タスクはJulepのワークフローシステムのコアです。これにより、エージェントが実行できる複雑な多段階のAIワークフローを定義できます。タスクコンポーネントの概要は次のとおりです: - -- **名前と説明**:各タスクには、簡単に識別できるように一意の名前と説明があります。 -- **主要なステップ**:タスクのコアであり、実行されるアクションのシーケンスを定義します。 -- **ツール**:タスク実行中にエージェントの能力を拡張するオプションの統合。 - -### ワークフローステップの種類 - -Julepのタスクには、さまざまな種類のステップを含めることができます: - -1. **プロンプト**:AIモデルにメッセージを送信し、応答を受け取ります。 - ```python - {"prompt": "Analyze the following data: {{data}}"} - ``` - -2. **ツール呼び出し**:統合されたツールまたはAPIを実行します。 - ```python - {"tool": "web_search", "arguments": {"query": "Latest AI developments"}} - ``` - -3. **評価**:計算を実行するか、データを操作します。 - ```python - {"evaluate": {"average_score": "sum(scores) / len(scores)"}} - ``` - -4. **条件ロジック**:条件に基づいてステップを実行します。 - ```python - {"if": "score > 0.8", "then": [...], "else": [...]} - ``` - -5. **ループ**:データを反復処理するか、ステップを繰り返します。 - ```python - {"foreach": {"in": "data_list", "do": [...]}} - ``` - -| ステップ名 | 説明 | 入力 | -|--------------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------| -| **プロンプト** | AIモデルにメッセージを送信し、応答を受け取ります。 | プロンプトテキストまたはテンプレート | -| **ツール呼び出し** | 統合されたツールまたはAPIを実行します。 | ツール名と引数 | -| **評価** | 計算を実行するか、データを操作します。 | 評価する式または変数 | -| **入力待ち** | 入力が受信されるまでワークフローを一時停止します。 | 必要なユーザーまたはシステム入力 | -| **ログ** | 指定された値またはメッセージを記録します。 | 記録するメッセージまたは値 | -| **埋め込み** | テキストを特定の形式またはシステムに埋め込みます。 | 埋め込むテキストまたはコンテンツ | -| **検索** | クエリに基づいてドキュメント検索を実行します。 | 検索クエリ | -| **取得** | キー値ストアから値を取得します。 | キー識別子 | -| **設定** | キー値ストアのキーに値を割り当てます。 | 割り当てるキーと値 | -| **並列** | 複数のステップを並行して実行します。 | 同時に実行するステップのリスト | -| **反復** | コレクションを反復処理し、各アイテムに対してステップを実行します。 | 反復するコレクションまたはリスト | -| **マップリデュース** | コレクションをマップし、式に基づいて結果をリデュースします。 | マップおよびリデュースするコレクションと式 | -| **条件分岐** | 条件に基づいてステップを実行します。 | 評価する条件 | -| **スイッチ** | 複数の条件に基づいてステップを実行します。スイッチケース文に似ています。 | 複数の条件と対応するステップ | -| **生成** | サブワークフローを実行し、その完了を待ちます。 | サブワークフロー識別子と入力データ | -| **エラー** | エラーメッセージを指定してエラーを処理します。 | エラーメッセージまたは処理指示 | -| **スリープ** | 指定された期間ワークフローを一時停止します。 | 期間(秒、分など) | -| **リターン** | ワークフローから値を返します。 | 返す値 | - -各ステップタイプの詳細情報と高度な使用法については、[タスクドキュメント](https://docs.julep.ai/tasks)を参照してください。 - -## 高度な機能 - -Julepは、AIワークフローを強化するための高度な機能を提供します: - -### エージェントにツールを追加する - -外部ツールやAPIを統合してエージェントの能力を拡張します: - -```python -client.agents.tools.create( - agent_id=agent.id, - name="web_search", - description="Search the web for information.", - integration={ - "provider": "google", - "method": "search", - "setup": {"api_key": "your_google_api_key"}, - }, -) -``` - -### セッションとユーザーの管理 - -Julepは、持続的なインタラクションのための強力なセッション管理を提供します: - -```python -session = client.sessions.create( - agent_id=agent.id, - user_id="user123", - context_overflow="adaptive" -) - -# 同じセッションで会話を続ける -response = client.sessions.chat( - session_id=session.id, - message="Follow up on our previous conversation." -) -``` - -### ドキュメントの統合と検索 - -エージェントのためのドキュメントを簡単に管理および検索します: - -```python -# ドキュメントをアップロードする -document = client.documents.create( - file="path/to/document.pdf", - metadata={"category": "research_paper"} -) - -# ドキュメントを検索する -results = client.documents.search( - query="AI advancements", - filter={"category": "research_paper"} -) -``` - -高度な機能と詳細な使用法については、[高度な機能ドキュメント](https://docs.julep.ai/advanced-features)を参照してください。 - -## SDKリファレンス - -- [Node.js SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) -- [Python SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) - -## APIリファレンス - -エージェント、タスク、および実行について詳しく学ぶために、包括的なAPIドキュメントを探索してください: - -- [エージェントAPI](https://api.julep.ai/api/docs#tag/agents) -- [タスクAPI](https://api.julep.ai/api/docs#tag/tasks) -- [実行API](https://api.julep.ai/api/docs#tag/executions) - -## 例とチュートリアル - -提供された例を基にして始めるのに役立つ例のプロジェクトとチュートリアルを見つけてください: - -- [例のプロジェクト](https://github.com/julep-ai/julep/tree/main/examples) -- [チュートリアル](https://docs.julep.ai/tutorials) - -## 貢献 - -プロジェクトへの貢献を歓迎します!貢献方法と行動規範を学びましょう: - -- [貢献ガイドライン](https://github.com/julep-ai/julep/blob/main/CONTRIBUTING.md) -- [行動規範](https://github.com/julep-ai/julep/blob/main/CODE_OF_CONDUCT.md) - -## サポートとコミュニティ - -コミュニティに参加して、助けを得たり、質問したり、アイデアを共有したりしましょう: - -- [Discord](https://discord.com/invite/JTSBGRZrzj) -- [GitHub Discussions](https://github.com/julep-ai/julep/discussions) -- [Twitter](https://twitter.com/julep_ai) - -## ライセンス - -このプロジェクトは[Apache License 2.0](https://github.com/julep-ai/julep/blob/main/LICENSE)の下でライセンスされています。 - -## 謝辞 - -貴重なリソースと貢献を提供してくれたすべての貢献者とオープンソースコミュニティに感謝します。 diff --git a/README.md b/README.md index e3a212efb..df3f1b56a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -English | [中文翻译](/README-CN.md) | [日本語翻訳](/README-JP.md) +[English](README.md) | [中文翻译](README-CN.md) | [日本語翻訳](README-JA.md) | [French](README-FR.md) -
- julep +
+ julep


- Explore Docs + Explore Docs (wip) · Discord · @@ -15,7 +15,6 @@ LinkedIn

-

NPM Version   @@ -26,101 +25,116 @@ GitHub License

-***** +--- > [!NOTE] -> 👨‍💻 Here for the devfest.ai event? Join our [Discord](https://discord.com/invite/JTSBGRZrzj) and check out the details below. +> Get your API key [here](https://dashboard-dev.julep.ai).
-🌟 Contributors and DevFest.AI Participants (Click to expand) +Contributions 🌟 (Click to expand) -## 🌟 Call for Contributors! +## Call for Contributors 🌟 We're excited to welcome new contributors to the Julep project! We've created several "good first issues" to help you get started. Here's how you can contribute: -1. Check out our [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines on how to contribute. +1. Check out our [CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/CONTRIBUTING.md) file for guidelines on how to contribute. 2. Browse our [good first issues](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to find a task that interests you. 3. If you have any questions or need help, don't hesitate to reach out on our [Discord](https://discord.com/invite/JTSBGRZrzj) channel. Your contributions, big or small, are valuable to us. Let's build something amazing together! 🚀 -### 🎉 DevFest.AI October 2024 - -Exciting news! We're participating in DevFest.AI throughout October 2024! 🗓️ - -- Contribute to Julep during this event and get a chance to win awesome Julep merch and swag! 🎁 -- Join developers from around the world in contributing to AI repositories and participating in amazing events. -- A big thank you to DevFest.AI for organizing this fantastic initiative! - -> [!TIP] -> Ready to join the fun? **[Tweet that you are participating](https://twitter.com/intent/tweet?text=Pumped%20to%20be%20participating%20in%20%40devfestai%20with%20%40julep_ai%20building%20%23ai%20%23agents%20%23workflows%20Let's%20gooo!%20https%3A%2F%2Fgit.new%2Fjulep)** and let's get coding! 🖥️ - -![Julep DevFest.AI](https://media.giphy.com/media/YjyUeyotft6epaMHtU/giphy.gif) -
-
-

📖 Table of Contents

+

📖 Table of Contents

+- [Call for Contributors 🌟](#call-for-contributors-) - [Introduction](#introduction) -- [Quick Example](#quick-example) - [Key Features](#key-features) -- [Why Julep vs. LangChain?](#why-julep-vs-langchain) - - [Different Use Cases](#different-use-cases) - - [Different Form Factor](#different-form-factor) - - [In Summary](#in-summary) +- [Quick Example](#quick-example) - [Installation](#installation) - [Python Quick Start 🐍](#python-quick-start-) - - [Step 1: Create an Agent](#step-1-create-an-agent) - - [Step 2: Create a Task that generates a story and comic strip](#step-2-create-a-task-that-generates-a-story-and-comic-strip) - - [Step 3: Execute the Task](#step-3-execute-the-task) - - [Step 4: Chat with the Agent](#step-4-chat-with-the-agent) - [Node.js Quick Start 🟩](#nodejs-quick-start-) - - [Step 1: Create an Agent](#step-1-create-an-agent-1) - - [Step 2: Create a Task that generates a story and comic strip](#step-2-create-a-task-that-generates-a-story-and-comic-strip-1) - - [Step 3: Execute the Task](#step-3-execute-the-task-1) - - [Step 4: Chat with the Agent](#step-4-chat-with-the-agent-1) - [Components](#components) - [Mental Model](#mental-model) - [Concepts](#concepts) - [Understanding Tasks](#understanding-tasks) + - [Lifecycle of a Task](#lifecycle-of-a-task) - [Types of Workflow Steps](#types-of-workflow-steps) -- [Advanced Features](#advanced-features) + - [Common Steps](#common-steps) + - [Key-Value Steps](#key-value-steps) + - [Iteration Steps](#iteration-steps) + - [Conditional Steps](#conditional-steps) + - [Other Control Flow](#other-control-flow) +- [Tool Types](#tool-types) + - [User-defined `functions`](#user-defined-functions) + - [`system` tools](#system-tools) + - [Available `system` resources and operations](#available-system-resources-and-operations) + - [Built-in `integrations`](#built-in-integrations) + - [Direct `api_calls`](#direct-api_calls) +- [Integrations](#integrations) +- [Other Features](#other-features) - [Adding Tools to Agents](#adding-tools-to-agents) - [Managing Sessions and Users](#managing-sessions-and-users) - [Document Integration and Search](#document-integration-and-search) -- [SDK Reference](#sdk-reference) -- [API Reference](#api-reference) +- [Reference](#reference) + - [SDK Reference](#sdk-reference) + - [API Reference](#api-reference) +- [Local Quickstart](#local-quickstart) +- [What's the difference between Julep and LangChain etc?](#whats-the-difference-between-julep-and-langchain-etc) + - [Different Use Cases](#different-use-cases) + - [Different Form Factor](#different-form-factor) + - [In Summary](#in-summary) -
## Introduction - +Julep is a platform for creating AI agents that remember past interactions and can perform complex tasks. It offers long-term memory and manages multi-step processes. + +Julep enables the creation of multi-step tasks incorporating decision-making, loops, parallel processing, and integration with numerous external tools and APIs. + +While many AI applications are limited to simple, linear chains of prompts and API calls with minimal branching, Julep is built to handle more complex scenarios which: + +- have multiple steps, +- make decisions based on model outputs, +- spawn parallel branches, +- use lots of tools, and +- run for a long time. + +> [!TIP] +> Imagine you want to build an AI agent that can do more than just answer simple questions—it needs to handle complex tasks, remember past interactions, and maybe even use other tools or APIs. That's where Julep comes in. Read [Understanding Tasks](#understanding-tasks) to learn more. + +## Key Features -Julep is a platform for creating AI agents that maintain state and execute complex workflows. It offers long-term context and orchestrates multi-step tasks. +1. 🧠 **Persistent AI Agents**: Remember context and information over long-term interactions. +2. 💾 **Stateful Sessions**: Keep track of past interactions for personalized responses. +3. 🔄 **Multi-Step Tasks**: Build complex, multi-step processes with loops and decision-making. +4. ⏳ **Task Management**: Handle long-running tasks that can run indefinitely. +5. 🛠️ **Built-in Tools**: Use built-in tools and external APIs in your tasks. +6. 🔧 **Self-Healing**: Julep will automatically retry failed steps, resend messages, and generally keep your tasks running smoothly. +7. 📚 **RAG**: Use Julep's document store to build a system for retrieving and using your own data. -Julep lets you define multi-step tasks that can include conditional logic, loops, parallel processing, and built-in integration with 100s of external tools and APIs. Typically AI applications tend to be linear and have simple chains of a handful of prompts and API calls without much branching or decision-making. +![features](https://github.com/user-attachments/assets/4355cbae-fcbd-4510-ac0d-f8f77b73af70) > [!TIP] -> Imagine you want to build an AI agent that can do more than just answer simple queries—it needs to handle complex tasks, remember past interactions, and maybe even integrate with other tools or APIs. That's where Julep comes in. +> Julep is ideal for applications that require AI use cases beyond simple prompt-response models. ## Quick Example Imagine a Research AI agent that can do the following: - 1. Take a topic, - 2. Come up with 100 search queries for that topic, - 3. Perform those web searches in parallel, - 4. Collect and compile the results, - 5. Come up with 5 follow-up questions, - 6. Repeat the process with new queries, - 7. Summarize the results, - 8. Send the summary to Discord -In julep, this would be a single task under 80 lines of code and run fully managed all on its own. Here's a working example: +1. **Take a topic**, +2. **Come up with 30 search queries** for that topic, +3. Perform those web **searches in parallel**, +4. **Summarize** the results, +5. Send the **summary to Discord**. + +> [!NOTE] +> In Julep, this would be a single task under 80 lines of code and run fully managed all on its own. All of the steps are executed on Julep's own servers and you don't need to lift a finger. + +Here's a working example: ```yaml name: Research Agent @@ -132,23 +146,26 @@ input_schema: topic: type: string description: The main topic to research + num_questions: + type: integer + description: The number of search queries to generate # Define the tools that the agent can use tools: -- name: web_search - type: integration - integration: - provider: brave - setup: - api_key: "YOUR_BRAVE_API_KEY" - -- name: discord_webhook - type: api_call - api_call: - url: "YOUR_DISCORD_WEBHOOK_URL" - method: POST - headers: - Content-Type: application/json + - name: web_search + type: integration + integration: + provider: brave + setup: + api_key: + + - name: discord_webhook + type: api_call + api_call: + url: https://discord.com/api/webhooks// + method: POST + headers: + Content-Type: application/json # Special variables: # - inputs: for accessing the input to the task @@ -161,7 +178,7 @@ main: - role: system content: >- You are a research assistant. - Generate 100 diverse search queries related to the topic: + Generate {{inputs[0].num_questions|default(30, true)}} diverse search queries related to the topic: {{inputs[0].topic}} Write one query per line. @@ -169,7 +186,7 @@ main: # Evaluate the search queries using a simple python expression - evaluate: - search_queries: "_.split('\n')" + search_queries: "_.split(NEWLINE)" # Run the web search in parallel for each query - over: "_.search_queries" @@ -177,410 +194,450 @@ main: tool: web_search arguments: query: "_" - on_error: - parallelism: 100 - -# Collect the results from the web search -- evaluate: - results: "'\n'.join([item.result for item in _])" - -# Generate follow-up questions based on the results -- prompt: - - role: system - content: >- - Based on the following research results, generate 5 follow-up questions that would deepen our understanding of {{inputs[0].topic}}: - {{_.results}} - - Write one question per line. - unwrap: true - -- evaluate: - follow_up_queries: "_.split('\n')" - -# Run the web search in parallel for each follow-up query -- over: "_.follow_up_queries" - map: - tool: web_search - arguments: - query: "_" - parallelism: 5 +# Collect the results from the web search - evaluate: - all_results: "outputs[3].results + '\n'.join([item.result for item in _])" + search_results: _ # Summarize the results - prompt: - role: system content: > - You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. - The summary should be well-structured, informative, and highlight key findings and insights: - {{_.all_results}} + You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. + The summary should be well-structured, informative, and highlight key findings and insights. Keep the summary concise and to the point. + The length of the summary should be less than 150 words. + Here are the search results: + {{_.search_results}} unwrap: true + settings: + model: gpt-4o-mini + +- evaluate: + discord_message: |- + f''' + **Research Summary for {inputs[0].topic}** + {_} + ''' -# Send the summary to Discord + # Send the summary to Discord - tool: discord_webhook arguments: - content: > - **Research Summary for {{inputs[0].topic}}** - - {{_}} + json_: + content: _.discord_message[:2000] # Discord has a 2000 character limit ``` -> [!TIP] -> Julep is really useful when you want to build AI agents that can maintain context and state over long-term interactions. It's great for designing complex, multi-step workflows and integrating various tools and APIs directly into your agent's processes. -> -> In this example, Julep will automatically manage parallel executions, retry failed steps, resend api requests, and keep the workflows running reliably until completion. - -## Key Features - -1. **Persistent AI Agents**: Persist context and state over long-term interactions. -2. **Stateful Sessions**: Remember past interactions for personalized responses. -3. **Multi-Step Workflows**: Build complex, multi-step processes with loops and conditional logic. -4. **Task Orchestration**: Manage long-running tasks that can run indefinitely. -5. **Built-in Tools**: Integrate built-in tools and external APIs into workflows. -6. **Self-Healing**: Julep will automatically retry failed steps, resend messages, and generally keep your workflows running smoothly. -7. **RAG**: Use Julep's document store to build a RAG system for your own data. - -Julep is ideal for applications that require AI use cases beyond simple prompt-response models. - -## Why Julep vs. LangChain? - -### Different Use Cases - -Think of LangChain and Julep as tools with different focuses within the AI development stack. - -LangChain is great for creating sequences of prompts and managing interactions with LLMs. It has a large ecosystem with lots of pre-built integrations, which makes it convenient if you want to get something up and running quickly. LangChain fits well with simple use cases that involve a linear chain of prompts and API calls. - -Julep, on the other hand, is more about building persistent AI agents that can maintain context over long-term interactions. It shines when you need complex workflows that involve multi-step tasks, conditional logic, and integration with various tools or APIs directly within the agent's process. It's designed from the ground up to manage persistent sessions and complex workflows. - -Use Julep if you imagine building a complex AI assistant that needs to: - -- Keep track of user interactions over days or weeks. -- Perform scheduled tasks, like sending daily summaries or monitoring data sources. -- Make decisions based on prior interactions or stored data. -- Interact with multiple external services as part of its workflow. - -Then Julep provides the infrastructure to support all that without you having to build it from scratch. - -### Different Form Factor - -Julep is a **platform** that includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. In order to build something with Julep, you write a description of the workflow in `YAML`, and then run the workflow in the cloud. - -Julep is built for heavy-lifting, multi-step, and long-running workflows and there's no limit to how complex the workflow can be. - -LangChain is a **library** that includes a few tools and a framework for building linear chains of prompts and tools. In order to build something with LangChain, you typically write Python code that configures and runs the model chains you want to use. +In this example, Julep will automatically manage parallel executions, retry failed steps, resend API requests, and keep the tasks running reliably until completion. -LangChain might be sufficient and quicker to implement for simple use cases that involve a linear chain of prompts and API calls. - -### In Summary +> This runs in under 30 seconds and returns the following output: -Use LangChain when you need to manage LLM interactions and prompt sequences in a stateless or short-term context. +
+Research Summary for AI (Click to expand) + +> **Research Summary for AI** +> +> ### Summary of Research Results on Artificial Intelligence (AI) +> +> #### Introduction +> +> The field of Artificial Intelligence (AI) has seen significant advancements in recent years, marked by the development of methods and technologies that enable machines to perceive their environment, learn from data, and make decisions. The primary focus of this summary is on the insights derived from various research findings related to AI. +> +> #### Key Findings +> +> 1. **Definition and Scope of AI**: +> +> - AI is defined as a branch of computer science focused on creating systems that can perform tasks requiring human-like intelligence, including learning, reasoning, and problem-solving (Wikipedia). +> - It encompasses various subfields, including machine learning, natural language processing, robotics, and computer vision. +> +> 2. **Impact and Applications**: +> +> - AI technologies are being integrated into numerous sectors, improving efficiency and productivity. Applications range from autonomous vehicles and healthcare diagnostics to customer service automation and financial forecasting (OpenAI). +> - Google's commitment to making AI beneficial for everyone highlights its potential to significantly improve daily life by enhancing user experiences across various platforms (Google AI). +> +> 3. **Ethical Considerations**: +> +> - There is an ongoing discourse regarding the ethical implications of AI, including concerns about privacy, bias, and accountability in decision-making processes. The need for a framework that ensures the safe and responsible use of AI technologies is emphasized (OpenAI). +> +> 4. **Learning Mechanisms**: +> +> - AI systems utilize different learning mechanisms, such as supervised learning, unsupervised learning, and reinforcement learning. These methods allow AI to improve performance over time by learning from past experiences and data (Wikipedia). +> - The distinction between supervised and unsupervised learning is critical; supervised learning relies on labeled data, while unsupervised learning identifies patterns without predefined labels (Unsupervised). +> +> 5. **Future Directions**: +> - Future AI developments are expected to focus on enhancing the interpretability and transparency of AI systems, ensuring that they can provide justifiable decisions and actions (OpenAI). +> - There is also a push towards making AI systems more accessible and user-friendly, encouraging broader adoption across different demographics and industries (Google AI). +> +> #### Conclusion +> +> AI represents a transformative force across multiple domains, promising to reshape industries and improve quality of life. However, as its capabilities expand, it is crucial to address the ethical and societal implications that arise. Continued research and collaboration among technologists, ethicists, and policymakers will be essential in navigating the future landscape of AI. -Choose Julep when you need a robust framework for stateful agents with advanced workflow capabilities, persistent sessions, and complex task orchestration. +
## Installation To get started with Julep, install it using [npm](https://www.npmjs.com/package/@julep/sdk) or [pip](https://pypi.org/project/julep/): +**Node.js**: + ```bash npm install @julep/sdk + +# or + +bun add @julep/sdk ``` -or +**Python**: ```bash pip install julep ``` > [!NOTE] -> ~~Get your API key [here](https://app.julep.ai/api-keys).~~ -> -> While we are in beta, you can reach out on [Discord](https://discord.com/invite/JTSBGRZrzj) to get your API key. +> Get your API key [here](https://dashboard-dev.julep.ai). +> +> While we are in beta, you can also reach out on [Discord](https://discord.com/invite/JTSBGRZrzj) to get rate limits lifted on your API key. > [!TIP] -> 💻 Are you a _show me the code!™_ kind of person? We have created a ton of cookbooks for you to get started with. **Check out the [cookbooks](/cookbooks)** to browse through examples. -> -> 💡 There's also lots of ideas that you can build on top of Julep. **Check out the [list of ideas](/cookbooks/IDEAS.md)** to get some inspiration. +> 💻 Are you a _show me the code!™_ kind of person? We have created a ton of cookbooks for you to get started with. **Check out the [cookbooks](https://github.com/julep-ai/julep/tree/dev/cookbooks)** to browse through examples. +> +> 💡 There's also lots of ideas that you can build on top of Julep. **Check out the [list of ideas](https://github.com/julep-ai/julep/tree/dev/cookbooks/IDEAS.md)** to get some inspiration. ## Python Quick Start 🐍 -### Step 1: Create an Agent +````python +### Step 0: Setup -```python +import time import yaml from julep import Julep # or AsyncJulep client = Julep(api_key="your_julep_api_key") +### Step 1: Create an Agent + agent = client.agents.create( name="Storytelling Agent", - model="gpt-4o", - about="You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", -) - -# 🛠️ Add an image generation tool (DALL·E) to the agent -client.agents.tools.create( - agent_id=agent.id, - name="image_generator", - description="Use this tool to generate images based on descriptions.", - integration={ - "provider": "dalle", - "method": "generate_image", - "setup": { - "api_key": "your_openai_api_key", - }, - }, + model="claude-3.5-sonnet", + about="You are a creative storyteller that crafts engaging stories on a myriad of topics.", ) -``` ### Step 2: Create a Task that generates a story and comic strip -Let's define a multi-step task to create a story and generate a paneled comic strip based on an input idea: - -```python -# 📋 Task -# Create a task that takes an idea and creates a story and a 4-panel comic strip task_yaml = """ -name: Story and Comic Creator -description: Create a story based on an idea and generate a 4-panel comic strip illustrating the story. +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + type: integration + integration: + provider: wikipedia + method: search main: - # Step 1: Generate a story and outline into 4 panels + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside ```yaml tags at the end of your response. + unwrap: true + + - evaluate: + plot_ideas: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + + # Step 2: Extract research fields from the plot ideas - prompt: - role: system content: You are {{agent.name}}. {{agent.about}} - role: user content: > - Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. - Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story. + Here are some plot ideas for a story: + {% for idea in _.plot_ideas %} + - {{idea}} + {% endfor %} + + To develop the story, we need to research for the plot ideas. + What should we research? Write down wikipedia search queries for the plot ideas you think are interesting. + Return your output as a yaml list inside ```yaml tags at the end of your response. unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 - # Step 2: Extract the panel descriptions and story - evaluate: - story: _.split('1. ')[0].strip() - panels: re.findall(r'\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)', _) + research_queries: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) - # Step 3: Generate images for each panel using the image generator tool + # Step 3: Research each plot idea - foreach: - in: _.panels + in: _.research_queries do: - tool: image_generator + tool: research_wikipedia arguments: - description: _ + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' - # Step 4: Generate a catchy title for the story + # Step 4: Think and deliberate - prompt: - role: system content: You are {{agent.name}}. {{agent.about}} - role: user - content: > - Based on the story below, generate a catchy title. - - Story: {{outputs[1].story}} + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside ```yaml tags at the end of your response. The yaml object should have the following structure: + + ```yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""``` + + Make sure the yaml is valid and the characters and scenes are not empty. Also take care of semicolons and other gotchas of writing yaml. unwrap: true - # Step 5: Return the story, the generated images, and the title - - return: - title: outputs[3] - story: outputs[1].story - comic_panels: "[output.image.url for output in outputs[2]]" + - evaluate: + plot: "load_yaml(_.split('```yaml')[1].split('```')[0].strip())" """ task = client.tasks.create( agent_id=agent.id, **yaml.safe_load(task_yaml) ) -``` ### Step 3: Execute the Task -```python -# 🚀 Execute the task with an input idea execution = client.executions.create( task_id=task.id, input={"idea": "A cat who learns to fly"} ) # 🎉 Watch as the story and comic panels are generated -for transition in client.executions.transitions.stream(execution_id=execution.id): - print(transition) +while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']: + print(result.status, result.output) + time.sleep(1) # 📦 Once the execution is finished, retrieve the results -result = client.executions.get(execution_id=execution.id) -``` - -### Step 4: Chat with the Agent - -Start an interactive chat session with the agent: +if result.status == "succeeded": + print(result.output) +else: + raise Exception(result.error) +```` -```python -session = client.sessions.create(agent_id=agent.id) - -# 💬 Send messages to the agent -while (message := input("Enter a message: ")) != "quit": - response = client.sessions.chat( - session_id=session.id, - message=message, - ) - - print(response) -``` - -> [!TIP] -> You can find the full python example [here](example.py). +You can find the full python example [here](example.py). +
+ + Back to Top +  |  + + Table of Contents + +
## Node.js Quick Start 🟩 -### Step 1: Create an Agent - ```javascript -import { Julep } from '@julep/sdk'; -import yaml from 'js-yaml'; +// Step 0: Setup +const dotenv = require("dotenv"); +const { Julep } = require("@julep/sdk"); +const yaml = require("yaml"); + +dotenv.config(); -const client = new Julep({ apiKey: 'your_julep_api_key' }); +const client = new Julep({ + apiKey: process.env.JULEP_API_KEY, + environment: process.env.JULEP_ENVIRONMENT || "production", +}); + +/* Step 1: Create an Agent */ async function createAgent() { const agent = await client.agents.create({ name: "Storytelling Agent", - model: "gpt-4", - about: "You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", - }); - - // 🛠️ Add an image generation tool (DALL·E) to the agent - await client.agents.tools.create(agent.id, { - name: "image_generator", - description: "Use this tool to generate images based on descriptions.", - integration: { - provider: "dalle", - method: "generate_image", - setup: { - api_key: "your_openai_api_key", - }, - }, + model: "claude-3.5-sonnet", + about: + "You are a creative storyteller that crafts engaging stories on a myriad of topics.", }); - return agent; } -``` -### Step 2: Create a Task that generates a story and comic strip +/* Step 2: Create a Task that generates a story and comic strip */ -```javascript const taskYaml = ` -name: Story and Comic Creator -description: Create a story based on an idea and generate a 4-panel comic strip illustrating the story. +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + integration: + provider: wikipedia + method: search main: - # Step 1: Generate a story and outline into 4 panels + # Step 1: Generate plot idea - prompt: - role: system content: You are {{agent.name}}. {{agent.about}} - role: user content: > - Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. - Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story. + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside \`\`\`yaml tags at the end of your response. unwrap: true - # Step 2: Extract the panel descriptions and story - evaluate: - story: _.split('1. ')[0].trim() - panels: _.match(/\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)/g) + plot_ideas: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) - # Step 3: Generate images for each panel using the image generator tool + # Step 2: Extract research fields from the plot ideas + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Here are some plot ideas for a story: + {% for idea in _.plot_ideas %} + - {{idea}} + {% endfor %} + + To develop the story, we need to research for the plot ideas. + What should we research? Write down wikipedia search queries for the plot ideas you think are interesting. + Return your output as a yaml list inside \`\`\`yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 + + - evaluate: + research_queries: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + + # Step 3: Research each plot idea - foreach: - in: _.panels + in: _.research_queries do: - tool: image_generator + tool: research_wikipedia arguments: - description: _ + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' - # Step 4: Generate a catchy title for the story + # Step 4: Think and deliberate - prompt: - role: system content: You are {{agent.name}}. {{agent.about}} - role: user - content: > - Based on the story below, generate a catchy title. - - Story: {{outputs[1].story}} + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside \`\`\`yaml tags at the end of your response. The yaml object should have the following structure: + + \`\`\`yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""\`\`\` + + Make sure the yaml is valid and the characters and scenes are not empty. Also take care of semicolons and other gotchas of writing yaml. unwrap: true - # Step 5: Return the story, the generated images, and the title - - return: - title: outputs[3] - story: outputs[1].story - comic_panels: outputs[2].map(output => output.image.url) + - evaluate: + plot: "load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip())" `; -async function createTask(agent) { - const task = await client.tasks.create(agent.id, yaml.load(taskYaml)); +async function createTask(agentId) { + const task = await client.tasks.create(agentId, yaml.parse(taskYaml)); return task; } -``` -### Step 3: Execute the Task +/* Step 3: Execute the Task */ -```javascript -async function executeTask(task) { - const execution = await client.executions.create(task.id, { - input: { idea: "A cat who learns to fly" } +async function executeTask(taskId) { + const execution = await client.executions.create(taskId, { + input: { idea: "A cat who learns to fly" }, }); // 🎉 Watch as the story and comic panels are generated - for await (const transition of client.executions.transitions.stream(execution.id)) { - console.log(transition); - } - - // 📦 Once the execution is finished, retrieve the results - const result = await client.executions.get(execution.id); - return result; -} -``` - -### Step 4: Chat with the Agent - -```javascript -async function chatWithAgent(agent) { - const session = await client.sessions.create({ agent_id: agent.id }); - - // 💬 Send messages to the agent - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - - const chat = async () => { - rl.question("Enter a message (or 'quit' to exit): ", async (message) => { - if (message.toLowerCase() === 'quit') { - rl.close(); - return; + while (true) { + const result = await client.executions.get(execution.id); + console.log(result.status, result.output); + + if (result.status === "succeeded" || result.status === "failed") { + // 📦 Once the execution is finished, retrieve the results + if (result.status === "succeeded") { + console.log(result.output); + } else { + throw new Error(result.error); } + break; + } - const response = await client.sessions.chat(session.id, { message }); - console.log(response); - chat(); - }); - }; - - chat(); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } } -// Run the example -async function runExample() { - const agent = await createAgent(); - const task = await createTask(agent); - const result = await executeTask(task); - console.log("Task Result:", result); - await chatWithAgent(agent); +// Main function to run the example +async function main() { + try { + const agent = await createAgent(); + const task = await createTask(agent.id); + await executeTask(task.id); + } catch (error) { + console.error("An error occurred:", error); + } } -runExample().catch(console.error); +main() + .then(() => console.log("Done")) + .catch(console.error); ``` -> [!TIP] -> You can find the full Node.js example [here](example.js). +You can find the full Node.js example [here](example.js). + +
+ + Back to Top +  |  + + Table of Contents + +
## Components @@ -592,13 +649,19 @@ Julep is made up of the following components: ### Mental Model +
+ +
+ Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's how to visualize it: 1. **Your Application Code:** - - You use the Julep SDK in your application to define agents, tasks, and workflows. + + - You can use the Julep SDK in your application to define agents, tasks, and workflows. - The SDK provides functions and classes that make it easy to set up and manage these components. 2. **Julep Backend Service:** + - The SDK communicates with the Julep backend over the network. - The backend handles execution of tasks, maintains session state, stores documents, and orchestrates workflows. @@ -606,11 +669,6 @@ Think of Julep as a platform that combines both client-side and server-side comp - Within your workflows, you can integrate external tools and services. - The backend facilitates these integrations, so your agents can, for example, perform web searches, access databases, or call third-party APIs. -In simpler terms: -- Julep is a platform for building stateful AI agents. -- You use the SDK (like a toolkit) in your code to define what your agents do. -- The backend service (which you can think of as the engine) runs these definitions, manages state, and handles complexity. - ## Concepts Julep is built on several key technical components that work together to create powerful AI workflows: @@ -628,7 +686,7 @@ graph TD classDef client fill:#9ff,stroke:#333,stroke-width:1px; class User client; - + classDef core fill:#f9f,stroke:#333,stroke-width:2px; class Agent,Tasks,Session core; ``` @@ -641,69 +699,818 @@ graph TD - **Documents**: Text or data objects associated with agents or users, vectorized and stored for semantic search and retrieval. - **Executions**: Instances of tasks that have been initiated with specific inputs, with their own lifecycle and state machine. -For a more detailed explanation of these concepts and their interactions, please refer to our [Concepts Documentation](/docs/julep-concepts.md). +
+ + Back to Top +  |  + + Table of Contents + +
## Understanding Tasks Tasks are the core of Julep's workflow system. They allow you to define complex, multi-step AI workflows that your agents can execute. Here's a brief overview of task components: -- **Name and Description**: Each task has a unique name and description for easy identification. -- **Main Steps**: The core of a task, defining the sequence of actions to be performed. +- **Name, Description and Input Schema**: Each task has a unique name and description for easy identification. An input schema (optional) that is used to validate the input to the task. +- **Main Steps**: The core of a task, defining the sequence of actions to be performed. Each step can be a prompt, tool call, evaluate, wait_for_input, log, get, set, foreach, map_reduce, if-else, switch, sleep, or return. (See [Types of Workflow Steps](#types-of-workflow-steps) for more details) - **Tools**: Optional integrations that extend the capabilities of your agent during task execution. +### Lifecycle of a Task + +You create a task using the Julep SDK and specify the main steps that the agent will execute. When you execute a task, the following lifecycle happens: + +```mermaid +sequenceDiagram + participant D as Your Code + participant C as Julep Client + participant S as Julep Server + + D->>C: Create Task + C->>S: Submit Execution + Note over S: Execute Task + Note over S: Manage State + S-->>C: Execution Events + C-->>D: Progress Updates + S->>C: Execution Completion + C->>D: Final Result +``` + ### Types of Workflow Steps -Tasks in Julep can include various types of steps: - -1. **Prompt**: Send a message to the AI model and receive a response. - ```python - {"prompt": "Analyze the following data: {{data}}"} - ``` - -2. **Tool Call**: Execute an integrated tool or API. - ```python - {"tool": "web_search", "arguments": {"query": "Latest AI developments"}} - ``` - -3. **Evaluate**: Perform calculations or manipulate data. - ```python - {"evaluate": {"average_score": "sum(scores) / len(scores)"}} - ``` - -4. **Conditional Logic**: Execute steps based on conditions. - ```python - {"if": "score > 0.8", "then": [...], "else": [...]} - ``` - -5. **Loops**: Iterate over data or repeat steps. - ```python - {"foreach": {"in": "data_list", "do": [...]}} - ``` - -| Step Name | Description | Input | -|--------------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------| -| **Prompt** | Send a message to the AI model and receive a response. | Prompt text or template | -| **Tool Call** | Execute an integrated tool or API. | Tool name and arguments | -| **Evaluate** | Perform calculations or manipulate data. | Expressions or variables to evaluate | -| **Wait for Input** | Pause workflow until input is received. | Any required user or system input | -| **Log** | Log a specified value or message. | Message or value to log | -| **Embed** | Embed text into a specific format or system. | Text or content to embed | -| **Search** | Perform a document search based on a query. | Search query | -| **Get** | Retrieve a value from a key-value store. | Key identifier | -| **Set** | Assign a value to a key in a key-value store. | Key and value to assign | -| **Parallel** | Run multiple steps in parallel. | List of steps to execute simultaneously | -| **Foreach** | Iterate over a collection and perform steps for each item. | Collection or list to iterate over | -| **MapReduce** | Map over a collection and reduce the results based on an expression. | Collection to map and reduce expressions | -| **If Else** | Conditional execution of steps based on a condition. | Condition to evaluate | -| **Switch** | Execute steps based on multiple conditions, similar to a switch-case statement. | Multiple conditions and corresponding steps | -| **Yield** | Run a subworkflow and await its completion. | Subworkflow identifier and input data | -| **Error** | Handle errors by specifying an error message. | Error message or handling instructions | -| **Sleep** | Pause the workflow for a specified duration. | Duration (seconds, minutes, etc.) | -| **Return** | Return a value from the workflow. | Value to return | - -For detailed information on each step type and advanced usage, please refer to our [Task Documentation](https://docs.julep.ai/tasks). - -## Advanced Features +Tasks in Julep can include various types of steps, allowing you to create complex and powerful workflows. Here's an overview of the available step types: + +#### Common Steps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAboutSyntax
Prompt +Send a message to the AI model and receive a response +

Note: The prompt step uses Jinja templates and you can access context variables in them. +
+ +```yaml +- prompt: "Analyze the following data: {{agent.name}}" # <-- this is a jinja template +``` + +```yaml +- prompt: + - role: system + content: "You are {{agent.name}}. {{agent.about}}" + - role: user + content: "Analyze the following data: {{_.data}}" +``` + +
Tool Call +Execute an integrated tool or API that you have previously declared in the task. +

Note: The tool call step uses Python expressions inside the arguments. + +
+ +```yaml +- tool: web_search + arguments: + query: '"Latest AI developments"' # <-- this is a python expression (notice the quotes) + num_results: len(_.topics) # <-- python expression to access the length of a list +``` + +
Evaluate +Perform calculations or manipulate data +

Note: The evaluate step uses Python expressions. +
+ +```yaml +- evaluate: + average_score: sum(scores) / len(scores) +``` + +
Wait for Input +Pause workflow until input is received. It accepts an `info` field that can be used by your application to collect input from the user. + +

Note: The wait_for_input step is useful when you want to pause the workflow and wait for user input e.g. to collect a response to a prompt. + +
+ +```yaml +- wait_for_input: + info: + message: '"Please provide additional information about {_.required_info}."' # <-- python expression to access the context variable +``` + +
Log +Log a specified value or message. + +

Note: The log step uses Jinja templates and you can access context variables in them. + +
+ +```yaml +- log: "Processing completed for item {{_.item_id}}" # <-- jinja template to access the context variable +``` + +
+ +#### Key-Value Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
Get +Retrieve a value from the execution's key-value store. + + + +```yaml +- get: user_preference +``` + +
Set +Assign a value to a key in the execution's key-value store. + +

Note: The set step uses Python expressions. + +
+ +```yaml +- set: + user_preference: '"dark_mode"' # <-- python expression +``` + +
+ +#### Iteration Steps + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Foreach +Iterate over a collection and perform steps for each item + + + +```yaml +- foreach: + in: _.data_list # <-- python expression to access the context variable + do: + - log: "Processing item {{_.item}}" # <-- jinja template to access the context variable +``` + +
Map-Reduce +Map over a collection and reduce the results + + + +```yaml +- map_reduce: + over: _.numbers # <-- python expression to access the context variable + map: + - evaluate: + squared: "_ ** 2" + reduce: results + [_] # <-- (optional) python expression to reduce the results. This is the default if omitted. +``` + +```yaml +- map_reduce: + over: _.topics + map: + - prompt: Write an essay on {{_}} + parallelism: 10 +``` + +
Parallel +Run multiple steps in parallel + + + +```yaml +- parallel: + - tool: web_search + arguments: + query: '"AI news"' + - tool: weather_check + arguments: + location: '"New York"' +``` + +
+ +#### Conditional Steps + + + + + + + + + + + + + + + + + +
Name About Syntax
If-Else +Conditional execution of steps + + + +```yaml +- if: _.score > 0.8 # <-- python expression + then: + - log: High score achieved + else: + - error: Score needs improvement +``` + +
Switch +Execute steps based on multiple conditions + + + +```yaml +- switch: + - case: _.category == 'A' + then: + - log: "Category A processing" + - case: _.category == 'B' + then: + - log: "Category B processing" + - case: _ # Default case + then: + - error: Unknown category +``` + +
+ +#### Other Control Flow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name About Syntax
Sleep +Pause the workflow for a specified duration + + + +```yaml +- sleep: + seconds: 30 + # minutes: 1 + # hours: 1 + # days: 1 +``` + +
Return +Return a value from the workflow + +

Note: The return step uses Python expressions. + +
+ +```yaml +- return: + result: '"Task completed successfully"' # <-- python expression + time: datetime.now().isoformat() # <-- python expression +``` + +
Yield +Run a subworkflow and await its completion + + + +```yaml +- yield: + workflow: process_data + arguments: + input_data: _.raw_data # <-- python expression +``` + +
Error +Handle errors by specifying an error message + + + +```yaml +- error: "Invalid input provided" # <-- Strings only +``` + +
+ +Each step type serves a specific purpose in building sophisticated AI workflows. This categorization helps in understanding the various control flows and operations available in Julep tasks. + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Tool Types + +Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a `web_search(query)` tool to search the Internet for some information. + +Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. + +Tools in julep can be one of: + +1. **User-defined `functions`**: These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. They need to be handled by the client. The workflow will pause until the client calls the function and gives the results back to julep. +2. **`system` tools**: Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. +3. **`integrations`**: Built-in third party tools that can be used to extend the capabilities of your agents. +4. **`api_calls`**: Direct api calls during workflow executions as tool calls. + +### User-defined `functions` + +These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + +```yaml +name: Example system tool task +description: List agents using system call + +tools: + - name: send_notification + description: Send a notification to the user + type: function + function: + parameters: + type: object + properties: + text: + type: string + description: Content of the notification + +main: + - tool: send_notification + arguments: + content: '"hi"' # <-- python expression +``` + +Whenever julep encounters a _user-defined function_, it pauses, giving control back to the client and waits for the client to run the function call and give the results back to julep. + +### `system` tools + +Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + +`system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + +For example, + +```yaml +name: Example system tool task +description: List agents using system call + +tools: + - name: list_agent_docs + description: List all docs for the given agent + type: system + system: + resource: agent + subresource: doc + operation: list + +main: + - tool: list_agents + arguments: + limit: 10 # <-- python expression +``` + +#### Available `system` resources and operations + +- `agent`: + + - `list`: List all agents. + - `get`: Get a single agent by id. + - `create`: Create a new agent. + - `update`: Update an existing agent. + - `delete`: Delete an existing agent. + +- `user`: + + - `list`: List all users. + - `get`: Get a single user by id. + - `create`: Create a new user. + - `update`: Update an existing user. + - `delete`: Delete an existing user. + +- `session`: + + - `list`: List all sessions. + - `get`: Get a single session by id. + - `create`: Create a new session. + - `update`: Update an existing session. + - `delete`: Delete an existing session. + - `chat`: Chat with a session. + - `history`: Get the chat history with a session. + +- `task`: + + - `list`: List all tasks. + - `get`: Get a single task by id. + - `create`: Create a new task. + - `update`: Update an existing task. + - `delete`: Delete an existing task. + +- `doc` (subresource for `agent` and `user`): + - `list`: List all documents. + - `create`: Create a new document. + - `delete`: Delete an existing document. + - `search`: Search for documents. + +Additional operations available for some resources: + +- `embed`: Embed a resource (specific resources not specified in the provided code). +- `change_status`: Change the status of a resource (specific resources not specified in the provided code). +- `chat`: Chat with a resource (specific resources not specified in the provided code). +- `history`: Get the chat history with a resource (specific resources not specified in the provided code). +- `create_or_update`: Create a new resource or update an existing one (specific resources not specified in the provided code). + +Note: The availability of these operations may vary depending on the specific resource and implementation details. + +> [!TIP] > **Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +### Built-in `integrations` + +Julep comes with a number of built-in integrations (as described in the section below). `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + +See [Integrations](#integrations) for details on the available integrations. + +> [!TIP] > **Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +### Direct `api_calls` + +julep can also directly make api calls during workflow executions as tool calls. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + +For example, + +```yaml +name: Example api_call task +tools: + - type: api_call + name: hello + api_call: + method: GET + url: https://httpbin.org/get + +main: + - tool: hello + arguments: + json: + test: _.input # <-- python expression +``` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Integrations + +Julep supports various integrations that extend the capabilities of your AI agents. Here's a list of available integrations and their supported arguments: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Brave Search + +```yaml +setup: + api_key: string # The API key for Brave Search + +arguments: + query: string # The search query for searching with Brave + +output: + result: string # The result of the Brave Search +``` + + + +**Example cookbook**: [cookbooks/02-sarcastic-news-headline-generator.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/02-sarcastic-news-headline-generator.ipynb) + +
BrowserBase + +```yaml +setup: + api_key: string # The API key for BrowserBase + project_id: string # The project ID for BrowserBase + session_id: string # (Optional) The session ID for BrowserBase + +arguments: + urls: list[string] # The URLs for loading with BrowserBase + +output: + documents: list # The documents loaded from the URLs +``` + + + +**Example cookbook**: [cookbooks/06-browser-use.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-browser-use.ipynb) + +
Email + +```yaml +setup: + host: string # The host of the email server + port: integer # The port of the email server + user: string # The username of the email server + password: string # The password of the email server + +arguments: + to: string # The email address to send the email to + from: string # The email address to send the email from + subject: string # The subject of the email + body: string # The body of the email + +output: + success: boolean # Whether the email was sent successfully +``` + + + +**Example cookbook**: [cookbooks/00-Devfest-Email-Assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/00-Devfest-Email-Assistant.ipynb) + +
Spider + +```yaml +setup: + spider_api_key: string # The API key for Spider + +arguments: + url: string # The URL for which to fetch data + mode: string # The type of crawlers (default: "scrape") + params: dict # (Optional) The parameters for the Spider API + +output: + documents: list # The documents returned from the spider +``` + + + +**Example cookbook**: [cookbooks/01-website-crawler.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) + +
Weather + +```yaml +setup: + openweathermap_api_key: string # The API key for OpenWeatherMap + +arguments: + location: string # The location for which to fetch weather data + +output: + result: string # The weather data for the specified location +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
Wikipedia + +```yaml +arguments: + query: string # The search query string + load_max_docs: integer # (Optional) Maximum number of documents to load. Default is 2. + +output: + documents: list # The documents returned from the Wikipedia search +``` + + + +**Example cookbook**: [cookbooks/03-trip-planning-assistant.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) + +
FFmpeg + +```yaml +arguments: + cmd: string # The FFmpeg command to execute + file: string # The base64 encoded file to process + +output: + fileoutput: string # The output file from the FFmpeg command in base64 encoding + result: boolean # Whether the FFmpeg command was executed successfully + mime_type: string # The MIME type of the output file +``` + +
Llama Parse + +```yaml +setup: + llamaparse_api_key: string # The API key for Llama Parse + params: dict # (Optional) Additional parameters for the Llama Parse integration + +arguments: + file: string | Array # The base64 encoded file to parse or an array of http/https URLs to load. + filename: string # (Optional). The filename of the file. Default is a random UUID. Only used if file is a base64 encoded string. + params: dict # (Optional) Additional parameters for the Llama Parse integration. Overrides the setup parameters. + base64: boolean # Whether the input file is base64 encoded. Default is false. + +output: + documents: list # The parsed data from the document +``` + +
Cloudinary + +```yaml + +method: media_upload | media_edit # The method to use for the Cloudinary integration + +setup: + cloudinary_cloud_name: string # Your Cloudinary cloud name + cloudinary_api_key: string # Your Cloudinary API key + cloudinary_api_secret: string # Your Cloudinary API secret + params: dict # (Optional) Additional parameters for the Cloudinary integration + +arguments: + file: string # The URL of the file upload. Only available for media_upload method. + upload_params: dict # (Optional) Additional parameters for the upload. Only available for media_upload method. + public_id: string # (Optional) The public ID for the file. For media_edit method it is MANDATORY. For media_upload method it is optional. Default is a random UUID. + transformation: list[dict] # The transformations to apply to the file. Only available for media_edit method. + return_base64: boolean # Whether to return the file in base64 encoding. Default is false. + +output: + url: string # The URL of the uploaded file. Only available for media_upload method. + meta_data: dict # Additional metadata from the upload response. Only available for media_upload method. + public_id: string # The public ID of the uploaded file. Only available for media_upload method. + transformed_url: string # (Optional) The transformed URL. Only available for media_edit method. + base64: string # (Optional) The base64 encoded file if return_base64 is true. +``` + + + +**Example cookbook**: [cookbooks/05-video-processing-with-natural-language.ipynb](https://github.com/julep-ai/julep/blob/dev/cookbooks/05-video-processing-with-natural-language.ipynb) + +
+ +For more details, refer to our [Integrations Documentation](#integrations). + +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Other Features Julep offers a range of advanced features to enhance your AI workflows: @@ -717,9 +1524,9 @@ client.agents.tools.create( name="web_search", description="Search the web for information.", integration={ - "provider": "google", + "provider": "brave", "method": "search", - "setup": {"api_key": "your_google_api_key"}, + "setup": {"api_key": "your_brave_api_key"}, }, ) ``` @@ -731,14 +1538,19 @@ Julep provides robust session management for persistent interactions: ```python session = client.sessions.create( agent_id=agent.id, - user_id="user123", + user_id=user.id, context_overflow="adaptive" ) # Continue conversation in the same session response = client.sessions.chat( session_id=session.id, - message="Follow up on our previous conversation." + messages=[ + { + "role": "user", + "content": "Follow up on the previous conversation." + } + ] ) ``` @@ -748,30 +1560,118 @@ Easily manage and search through documents for your agents: ```python # Upload a document -document = client.documents.create( +document = client.agents.docs.create( title="AI advancements", content="AI is changing the world...", metadata={"category": "research_paper"} ) # Search documents -results = client.documents.search( - query="AI advancements", +results = client.agents.docs.search( + text="AI advancements", metadata_filter={"category": "research_paper"} ) ``` -For more advanced features and detailed usage, please refer to our [Advanced Features Documentation](https://docs.julep.ai/advanced-features). +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Reference + +### SDK Reference + +- **Node.js** [SDK Reference](https://github.com/julep-ai/node-sdk/blob/main/api.md) | [NPM Package](https://www.npmjs.com/package/@julep/sdk) +- **Python** [SDK Reference](https://github.com/julep-ai/python-sdk/blob/main/api.md) | [PyPI Package](https://pypi.org/project/julep/) -## SDK Reference +### API Reference -- [Node.js SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) -- [Python SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) +Explore our API documentation to learn more about agents, tasks, and executions: -## API Reference +- [Agents API](https://dev.julep.ai/api/docs#tag/agents) +- [Tasks API](https://dev.julep.ai/api/docs#tag/tasks) +- [Executions API](https://dev.julep.ai/api/docs#tag/executions) -Explore our comprehensive API documentation to learn more about agents, tasks, and executions: +
+ + Back to Top +  |  + + Table of Contents + +
+ +## Local Quickstart + +**Requirements**: + +- latest docker compose installed + +**Steps**: + +1. `git clone https://github.com/julep-ai/julep.git` +2. `cd julep` +3. `docker volume create cozo_backup` +4. `docker volume create cozo_data` +5. `cp .env.example .env # <-- Edit this file` +6. `docker compose --env-file .env --profile temporal-ui --profile single-tenant --profile self-hosted-db up --build` + +
+ + Back to Top +  |  + + Table of Contents + +
+ +--- + +## What's the difference between Julep and LangChain etc? + +### Different Use Cases + +Think of LangChain and Julep as tools with different focuses within the AI development stack. -- [Agents API](https://api.julep.ai/api/docs#tag/agents) -- [Tasks API](https://api.julep.ai/api/docs#tag/tasks) -- [Executions API](https://api.julep.ai/api/docs#tag/executions) +LangChain is great for creating sequences of prompts and managing interactions with LLMs. It has a large ecosystem with lots of pre-built integrations, which makes it convenient if you want to get something up and running quickly. LangChain fits well with simple use cases that involve a linear chain of prompts and API calls. + +Julep, on the other hand, is more about building persistent AI agents that can maintain context over long-term interactions. It shines when you need complex workflows that involve multi-step tasks, conditional logic, and integration with various tools or APIs directly within the agent's process. It's designed from the ground up to manage persistent sessions and complex workflows. + +Use Julep if you imagine building a complex AI assistant that needs to: + +- Keep track of user interactions over days or weeks. +- Perform scheduled tasks, like sending daily summaries or monitoring data sources. +- Make decisions based on prior interactions or stored data. +- Interact with multiple external services as part of its workflow. + +Then Julep provides the infrastructure to support all that without you having to build it from scratch. + +### Different Form Factor + +Julep is a **platform** that includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. In order to build something with Julep, you write a description of the workflow in `YAML`, and then run the workflow in the cloud. + +Julep is built for heavy-lifting, multi-step, and long-running workflows and there's no limit to how complex the workflow can be. + +LangChain is a **library** that includes a few tools and a framework for building linear chains of prompts and tools. In order to build something with LangChain, you typically write Python code that configures and runs the model chains you want to use. + +LangChain might be sufficient and quicker to implement for simple use cases that involve a linear chain of prompts and API calls. + +### In Summary + +Use LangChain when you need to manage LLM interactions and prompt sequences in a stateless or short-term context. + +Choose Julep when you need a robust framework for stateful agents with advanced workflow capabilities, persistent sessions, and complex task orchestration. + +
+ + Back to Top +  |  + + Table of Contents + +
diff --git a/SCRATCHPAD.md b/SCRATCHPAD.md new file mode 100644 index 000000000..f793fb367 --- /dev/null +++ b/SCRATCHPAD.md @@ -0,0 +1,137 @@ +Sure! So imagine you want to build an AI agent that can do more than just answer simple queries—it needs to handle complex tasks, remember past interactions, and maybe even integrate with other tools or APIs. That's where Julep comes in. It's an open-source platform that lets you create persistent AI agents with customizable workflows, making it super easy to develop and deploy advanced AI applications without reinventing the wheel. + + + +Julep is really useful when you want to build AI agents that can maintain context and state over long-term interactions. It's great for designing complex, multi-step workflows and integrating various tools and APIs directly into your agent's processes. + +Compared to LangChain, which is excellent for chaining together prompts and managing LLM interactions, Julep focuses more on creating persistent agents with customizable workflows. While LangChain provides a robust framework for building applications with language models, it doesn't inherently offer the same level of session management or state persistence that Julep does. + + + + + +Persistent sessions in Julep mean that the AI agents can maintain context and state over long periods and multiple interactions. So instead of just handling a single query and forgetting everything afterward (which is what you'd get with regular sessions), the agent can remember past conversations, user preferences, and any relevant data from previous interactions. This is super handy when you want your agent to provide a more personalized experience or when the tasks require building upon previous steps. + +For example, if you're building a customer support agent, it can recall a user's issue from earlier chats without them having to repeat themselves. Regular sessions typically don't offer this level of continuity. + +As for complex workflows, Julep lets you define multi-step tasks that can include conditional logic, loops, parallel processing, and integration with external tools or APIs. Regular workflows might be more linear and straightforward—think a simple sequence of prompts or API calls without much branching or decision-making capability. + +In Julep, you can create tasks where the agent might, say, take user input, perform a web search, process the results, maybe even interact with other services like sending an email or updating a database—all within a single workflow. This level of complexity allows you to build more sophisticated applications without having to manage the orchestration logic yourself. + +That said, one thing to keep in mind is that while Julep offers these advanced features, it's still relatively new compared to something like LangChain. So you might find that the community support and pre-built integrations aren't as extensive yet. If you need something up and running quickly with lots of existing modules, LangChain might be more convenient. But if you want more control over persistent state and complex task execution, Julep provides a solid framework for that. + + + + + + +LangChain is great for creating sequences of prompts and managing interactions with LLMs. It has a large ecosystem with lots of pre-built integrations, which makes it convenient if you want to get something up and running quickly. + +Julep, on the other hand, is more about building persistent AI agents that can maintain context over long-term interactions. It shines when you need complex workflows that involve multi-step tasks, conditional logic, and integration with various tools or APIs directly within the agent's process. + +So, you shouldn't think of Julep as a direct replacement for LangChain. Instead, consider it as an alternative that's better suited for projects where maintaining state over time and handling complex task executions are important. If your application requires agents that can remember past interactions, personalize responses, and perform intricate operations, Julep might be the way to go. + + + + +Think of LangChain and Julep as tools with different focuses within the AI development stack. + +LangChain is like a powerful library that helps you chain together prompts and manage interactions with language models. It's excellent for building applications where the primary interaction is between the user and the LLM in a sequential manner. You get utilities for prompt management, memory, and even some basic tools integration. But when it comes to handling more complex state management or long-term sessions, you might find yourself writing a lot of custom code. + +Julep, on the other hand, is more of an orchestration platform for AI agents. It's designed from the ground up to manage persistent sessions and complex workflows. Here's how you might think about it: + +Persistent State and Sessions: Julep allows your AI agents to maintain state over time without you having to implement the storage and retrieval mechanisms yourself. So if your application requires the agent to remember previous interactions, user preferences, or intermediate data across sessions, Julep handles that natively. + +Complex Workflow Management: With Julep, you can define multi-step tasks that include conditional logic, loops, parallel processing, and more. It's like having a built-in workflow engine tailored for AI agents. This is particularly useful when your agent needs to perform a series of actions that depend on each other or on external inputs. + +Tool and API Integration: While LangChain allows for some tools integration, Julep provides a more seamless way to incorporate external tools and APIs directly into your agent's workflows. You can define tools once and reuse them across different tasks and agents. + +Agent-Centric Design: Julep is centered around the concept of agents that have their own identities, abilities, and long-term goals. This abstraction is helpful when you're building applications where the agent needs to interact with users or systems in a more autonomous or stateful way. + +So, rather than a drop-in replacement, Julep can be thought of as a higher-level framework that sits on top of or alongside tools like LangChain. If you imagine building a complex AI assistant that needs to: + +Keep track of user interactions over days or weeks. +Perform scheduled tasks, like sending daily summaries or monitoring data sources. +Make decisions based on prior interactions or stored data. +Interact with multiple external services as part of its workflow. +Then Julep provides the infrastructure to support all that without you having to build it from scratch. + +In contrast, if you're building a simpler application where you just need to chain a few prompts together and don't require persistent state or complex task management, LangChain might be sufficient and quicker to implement. + +In summary: + +Use LangChain when you need to manage LLM interactions and prompt sequences in a stateless or short-term context. +Choose Julep when you need a robust framework for stateful agents with advanced workflow capabilities, persistent sessions, and complex task orchestration. + + + + +Sure! To help you "bookmark" Julep in your mind, here are some key phrases and keywords that set it apart and highlight where it uniquely shines: + +Persistent AI Agents: Julep specializes in creating AI agents that maintain context and state over long-term interactions. + +Stateful Sessions: Think of Julep when you need sessions that remember past interactions, enabling personalized and context-aware responses. + +Customizable Workflows: It's ideal for building complex, multi-step workflows with features like conditional logic, loops, and parallel processing. + +Advanced Task Orchestration: Julep excels at orchestrating intricate tasks without requiring you to build the orchestration logic from scratch. + +Seamless Tool and API Integration: Use Julep when you want to integrate external tools and APIs directly into your agent's workflows. + +Agent-Centric Design: It's designed around autonomous agents with their own identities and long-term goals, perfect for applications needing more than simple prompt-response interactions. + +Reason to Recall Julep: + +When you're working on a project that requires an AI agent to: + +Maintain Long-Term Context: Remember user preferences or past interactions over extended periods. + +Handle Complex Operations: Perform multi-step tasks that involve decision-making, branching logic, or interacting with multiple services. + +Integrate with External Systems: Seamlessly connect with APIs, databases, or other tools as part of the agent's workflow. + +Provide Personalized Experiences: Offer responses or actions tailored to individual users based on accumulated context. + +In these scenarios, Julep uniquely shines by providing the infrastructure and tools needed to build such sophisticated, stateful AI applications without having to build the underlying systems yourself. + + + + + + +Possible Descriptions: + +"Julep is an open-source platform for building persistent AI agents that maintain long-term context and execute complex, customizable workflows." + +"Create AI agents that remember past interactions and handle intricate tasks with Julep's customizable workflows and seamless tool integration." + +"Julep enables developers to build stateful AI agents with advanced task orchestration, maintaining context over time and integrating external tools directly into workflows." + +"With Julep, develop AI agents that persist over sessions, perform multi-step tasks, and integrate various tools and APIs effortlessly." + +"Julep is a platform for creating AI agents that maintain state and execute complex workflows, offering long-term context and advanced orchestration capabilities." + + + + +Key Concepts to Include: + +Persistent AI Agents: Agents that maintain context and state over long-term interactions. +Customizable Workflows: Ability to define complex, multi-step tasks with conditional logic, loops, and more. +Seamless Tool and API Integration: Direct integration of external tools and APIs into agents' workflows. +Stateful Sessions: Sessions that remember past interactions for personalized and context-aware responses. +Advanced Task Orchestration: Orchestrate intricate tasks without building the underlying logic from scratch. + + + +Top 5 Winners +1.5 "Julep: Open-source platform for AI agents with long-term memory and complex workflows." + +2.5 "Julep: Create AI agents that remember and handle intricate tasks effortlessly." + +3.5 "Julep: Create AI agents with persistent context and advanced orchestration." + +4.5 "Julep: Craft AI agents that persist and perform complex tasks seamlessly." + +5.5 "Julep: Build AI agents with persistent state and powerful task execution." + diff --git a/agents-api/.DS_Store b/agents-api/.DS_Store deleted file mode 100644 index 04526ed47..000000000 Binary files a/agents-api/.DS_Store and /dev/null differ diff --git a/agents-api/.gitignore b/agents-api/.gitignore index bc51c65f8..33217a796 100644 --- a/agents-api/.gitignore +++ b/agents-api/.gitignore @@ -5,6 +5,10 @@ temporal.db *.dat *.dir +# jupyterlab stuff +notebooks/Untitled*.ipynb +.virtual_documents/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/agents-api/Dockerfile b/agents-api/Dockerfile index 90432df12..d73bafe6b 100644 --- a/agents-api/Dockerfile +++ b/agents-api/Dockerfile @@ -13,8 +13,33 @@ RUN pip install 'poetry>=1.8.0,<1.9.0' \ COPY pyproject.toml poetry.lock ./ +RUN apt-get update && apt-get install -y --no-install-recommends \ + dpkg-dev \ + gcc \ + g++ \ + libboost-all-dev \ + libbluetooth-dev \ + libbz2-dev \ + libc6-dev \ + libexpat1-dev \ + libffi-dev \ + libgdbm-dev \ + liblzma-dev \ + libncursesw5-dev \ + libreadline-dev \ + libsqlite3-dev \ + libssl-dev \ + make \ + tk-dev \ + uuid-dev \ + wget \ + xz-utils \ + zlib1g-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + RUN poetry install --no-dev --no-root COPY . ./ -ENTRYPOINT ["python", "-m", "agents_api.web", "--host", "0.0.0.0", "--port", "8080"] +ENTRYPOINT ["gunicorn", "agents_api.web:app", "-c", "gunicorn_conf.py"] \ No newline at end of file diff --git a/agents-api/Dockerfile.worker b/agents-api/Dockerfile.worker index 0bf84fae2..292ca8ea5 100644 --- a/agents-api/Dockerfile.worker +++ b/agents-api/Dockerfile.worker @@ -13,6 +13,31 @@ RUN pip install 'poetry>=1.8.0,<1.9.0' \ COPY pyproject.toml poetry.lock ./ +RUN apt-get update && apt-get install -y --no-install-recommends \ + dpkg-dev \ + gcc \ + g++ \ + libboost-all-dev \ + libbluetooth-dev \ + libbz2-dev \ + libc6-dev \ + libexpat1-dev \ + libffi-dev \ + libgdbm-dev \ + liblzma-dev \ + libncursesw5-dev \ + libreadline-dev \ + libsqlite3-dev \ + libssl-dev \ + make \ + tk-dev \ + uuid-dev \ + wget \ + xz-utils \ + zlib1g-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + RUN poetry install --no-dev --no-root COPY . ./ diff --git a/agents-api/README.md b/agents-api/README.md index 7d6811ff2..60a5ea89b 100644 --- a/agents-api/README.md +++ b/agents-api/README.md @@ -46,8 +46,8 @@ This guide provides a brief overview for new contributors to get started with th ## Schema -### Relation `agent_default_settings` ``` +[agent_default_settings] ┌───┬────────────────────┬────────┬─────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼────────────────────┼────────┼─────────┼─────────────┤ @@ -61,10 +61,8 @@ This guide provides a brief overview for new contributors to get started with th │ 7 │ min_p │ False │ Float │ True │ │ 8 │ preset │ False │ String? │ True │ └───┴────────────────────┴────────┴─────────┴─────────────┘ -``` -### Relation `agents` -``` +[agents] ┌───┬──────────────┬────────┬──────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────┼────────┼──────────┼─────────────┤ @@ -78,10 +76,8 @@ This guide provides a brief overview for new contributors to get started with th │ 7 │ updated_at │ False │ Float │ True │ │ 8 │ metadata │ False │ Json │ True │ └───┴──────────────┴────────┴──────────┴─────────────┘ -``` -### Relation `developers` -``` +[developers] ┌───┬──────────────┬────────┬──────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────┼────────┼──────────┼─────────────┤ @@ -93,10 +89,8 @@ This guide provides a brief overview for new contributors to get started with th │ 5 │ created_at │ False │ Float │ True │ │ 6 │ updated_at │ False │ Float │ True │ └───┴──────────────┴────────┴──────────┴─────────────┘ -``` -### Relation `docs` -``` +[docs] ┌───┬────────────┬────────┬────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼────────────┼────────┼────────┼─────────────┤ @@ -107,28 +101,25 @@ This guide provides a brief overview for new contributors to get started with th │ 4 │ created_at │ False │ Float │ True │ │ 5 │ metadata │ False │ Json │ True │ └───┴────────────┴────────┴────────┴─────────────┘ -``` - -### Relation `entries` -``` -┌───┬─────────────┬────────┬─────────┬─────────────┐ -│ │ column │ is_key │ type │ has_default │ -├───┼─────────────┼────────┼─────────┼─────────────┤ -│ 0 │ session_id │ True │ Uuid │ False │ -│ 1 │ entry_id │ True │ Uuid │ True │ -│ 2 │ source │ True │ String │ False │ -│ 3 │ role │ True │ String │ False │ -│ 4 │ name │ True │ String? │ True │ -│ 5 │ content │ False │ [Json] │ False │ -│ 6 │ token_count │ False │ Int │ False │ -│ 7 │ tokenizer │ False │ String │ False │ -│ 8 │ created_at │ False │ Float │ True │ -│ 9 │ timestamp │ False │ Float │ True │ -└───┴─────────────┴────────┴─────────┴─────────────┘ -``` -### Relation `executions` -``` +[entries] +┌────┬──────────────┬────────┬─────────┬─────────────┐ +│ │ column │ is_key │ type │ has_default │ +├────┼──────────────┼────────┼─────────┼─────────────┤ +│ 0 │ session_id │ True │ Uuid │ False │ +│ 1 │ entry_id │ True │ Uuid │ True │ +│ 2 │ source │ True │ String │ False │ +│ 3 │ role │ True │ String │ False │ +│ 4 │ name │ True │ String? │ True │ +│ 5 │ content │ False │ String │ False │ +│ 6 │ tool_call_id │ False │ String? │ True │ +│ 7 │ token_count │ False │ Int │ False │ +│ 8 │ tokenizer │ False │ String │ False │ +│ 9 │ created_at │ False │ Float │ True │ +│ 10 │ timestamp │ False │ Float │ True │ +└────┴──────────────┴────────┴─────────┴─────────────┘ + +[executions] ┌───┬──────────────┬────────┬─────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────┼────────┼─────────┼─────────────┤ @@ -143,10 +134,22 @@ This guide provides a brief overview for new contributors to get started with th │ 8 │ created_at │ False │ Float │ True │ │ 9 │ updated_at │ False │ Float │ True │ └───┴──────────────┴────────┴─────────┴─────────────┘ -``` -### Relation `memories` -``` +[files] +┌───┬──────────────┬────────┬─────────┬─────────────┐ +│ │ column │ is_key │ type │ has_default │ +├───┼──────────────┼────────┼─────────┼─────────────┤ +│ 0 │ developer_id │ True │ Uuid │ False │ +│ 1 │ file_id │ True │ Uuid │ False │ +│ 2 │ name │ False │ String │ False │ +│ 3 │ description │ False │ String │ True │ +│ 4 │ mime_type │ False │ String? │ True │ +│ 5 │ size │ False │ Int │ False │ +│ 6 │ hash │ False │ String │ False │ +│ 7 │ created_at │ False │ Float │ True │ +└───┴──────────────┴────────┴─────────┴─────────────┘ + +[memories] ┌───┬──────────────────┬────────┬────────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────────┼────────┼────────────┼─────────────┤ @@ -159,10 +162,8 @@ This guide provides a brief overview for new contributors to get started with th │ 6 │ created_at │ False │ Float │ True │ │ 7 │ embedding │ False │ ? │ True │ └───┴──────────────────┴────────┴────────────┴─────────────┘ -``` -### Relation `memory_lookup` -``` +[memory_lookup] ┌───┬───────────┬────────┬───────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼───────────┼────────┼───────┼─────────────┤ @@ -170,10 +171,8 @@ This guide provides a brief overview for new contributors to get started with th │ 1 │ user_id │ True │ Uuid? │ True │ │ 2 │ memory_id │ True │ Uuid │ False │ └───┴───────────┴────────┴───────┴─────────────┘ -``` -### Relation `relations` -``` +[relations] ┌───┬──────────┬────────┬────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────┼────────┼────────┼─────────────┤ @@ -181,20 +180,16 @@ This guide provides a brief overview for new contributors to get started with th │ 1 │ relation │ True │ String │ False │ │ 2 │ tail │ True │ Uuid │ False │ └───┴──────────┴────────┴────────┴─────────────┘ -``` -### Relation `session_cache` -``` +[session_cache] ┌───┬────────┬────────┬────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼────────┼────────┼────────┼─────────────┤ │ 0 │ key │ True │ String │ False │ │ 1 │ value │ False │ Json │ False │ └───┴────────┴────────┴────────┴─────────────┘ -``` -### Relation `session_lookup` -``` +[session_lookup] ┌───┬──────────────────┬────────┬────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────────┼────────┼────────┼─────────────┤ @@ -202,28 +197,25 @@ This guide provides a brief overview for new contributors to get started with th │ 1 │ participant_type │ True │ String │ False │ │ 2 │ participant_id │ True │ Uuid │ False │ └───┴──────────────────┴────────┴────────┴─────────────┘ -``` -### Relation `sessions` -``` -┌───┬──────────────────┬────────┬──────────┬─────────────┐ -│ │ column │ is_key │ type │ has_default │ -├───┼──────────────────┼────────┼──────────┼─────────────┤ -│ 0 │ developer_id │ True │ Uuid │ False │ -│ 1 │ session_id │ True │ Uuid │ False │ -│ 2 │ updated_at │ True │ Validity │ True │ -│ 3 │ situation │ False │ String │ False │ -│ 4 │ summary │ False │ String? │ True │ -│ 5 │ created_at │ False │ Float │ True │ -│ 6 │ metadata │ False │ Json │ True │ -│ 7 │ render_templates │ False │ Bool │ True │ -│ 8 │ token_budget │ False │ Int? │ True │ -│ 9 │ context_overflow │ False │ String? │ True │ -└───┴──────────────────┴────────┴──────────┴─────────────┘ -``` - -### Relation `snippets` -``` +[sessions] +┌────┬────────────────────┬────────┬──────────┬─────────────┐ +│ │ column │ is_key │ type │ has_default │ +├────┼────────────────────┼────────┼──────────┼─────────────┤ +│ 0 │ developer_id │ True │ Uuid │ False │ +│ 1 │ session_id │ True │ Uuid │ False │ +│ 2 │ updated_at │ True │ Validity │ True │ +│ 3 │ situation │ False │ String │ False │ +│ 4 │ summary │ False │ String? │ True │ +│ 5 │ created_at │ False │ Float │ True │ +│ 6 │ metadata │ False │ Json │ True │ +│ 7 │ render_templates │ False │ Bool │ True │ +│ 8 │ token_budget │ False │ Int? │ True │ +│ 9 │ context_overflow │ False │ String? │ True │ +│ 10 │ forward_tool_calls │ False │ Bool? │ True │ +└────┴────────────────────┴────────┴──────────┴─────────────┘ + +[snippets] ┌───┬───────────┬────────┬─────────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼───────────┼────────┼─────────────┼─────────────┤ @@ -232,10 +224,8 @@ This guide provides a brief overview for new contributors to get started with th │ 2 │ content │ False │ String │ False │ │ 3 │ embedding │ False │ ? │ True │ └───┴───────────┴────────┴─────────────┴─────────────┘ -``` -### Relation `tasks` -``` +[tasks] ┌────┬───────────────┬────────┬──────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├────┼───────────────┼────────┼──────────┼─────────────┤ @@ -251,10 +241,8 @@ This guide provides a brief overview for new contributors to get started with th │ 9 │ created_at │ False │ Float │ True │ │ 10 │ metadata │ False │ Json │ True │ └────┴───────────────┴────────┴──────────┴─────────────┘ -``` -### Relation `temporal_executions_lookup` -``` +[temporal_executions_lookup] ┌───┬────────────────────────┬────────┬─────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼────────────────────────┼────────┼─────────┼─────────────┤ @@ -265,25 +253,22 @@ This guide provides a brief overview for new contributors to get started with th │ 4 │ result_run_id │ False │ String? │ False │ │ 5 │ created_at │ False │ Float │ True │ └───┴────────────────────────┴────────┴─────────┴─────────────┘ -``` -### Relation `tools` -``` -┌───┬────────────┬────────┬────────┬─────────────┐ -│ │ column │ is_key │ type │ has_default │ -├───┼────────────┼────────┼────────┼─────────────┤ -│ 0 │ agent_id │ True │ Uuid │ False │ -│ 1 │ tool_id │ True │ Uuid │ False │ -│ 2 │ type │ False │ String │ False │ -│ 3 │ name │ False │ String │ False │ -│ 4 │ spec │ False │ Json │ False │ -│ 5 │ updated_at │ False │ Float │ True │ -│ 6 │ created_at │ False │ Float │ True │ -└───┴────────────┴────────┴────────┴─────────────┘ -``` +[tools] +┌───┬─────────────┬────────┬─────────┬─────────────┐ +│ │ column │ is_key │ type │ has_default │ +├───┼─────────────┼────────┼─────────┼─────────────┤ +│ 0 │ agent_id │ True │ Uuid │ False │ +│ 1 │ tool_id │ True │ Uuid │ False │ +│ 2 │ type │ False │ String │ False │ +│ 3 │ name │ False │ String │ False │ +│ 4 │ description │ False │ String? │ False │ +│ 5 │ spec │ False │ Json │ False │ +│ 6 │ updated_at │ False │ Float │ True │ +│ 7 │ created_at │ False │ Float │ True │ +└───┴─────────────┴────────┴─────────┴─────────────┘ -### Relation `transitions` -``` +[transitions] ┌───┬───────────────┬────────┬───────────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼───────────────┼────────┼───────────────┼─────────────┤ @@ -298,10 +283,8 @@ This guide provides a brief overview for new contributors to get started with th │ 8 │ created_at │ False │ Float │ True │ │ 9 │ updated_at │ False │ Float │ True │ └───┴───────────────┴────────┴───────────────┴─────────────┘ -``` -### Relation `users` -``` +[users] ┌───┬──────────────┬────────┬────────┬─────────────┐ │ │ column │ is_key │ type │ has_default │ ├───┼──────────────┼────────┼────────┼─────────────┤ @@ -313,6 +296,5 @@ This guide provides a brief overview for new contributors to get started with th │ 5 │ updated_at │ False │ Float │ True │ │ 6 │ metadata │ False │ Json │ True │ └───┴──────────────┴────────┴────────┴─────────────┘ -``` - +``` diff --git a/agents-api/agents_api/__init__.py b/agents-api/agents_api/__init__.py index 744ed4960..dfe10ea38 100644 --- a/agents-api/agents_api/__init__.py +++ b/agents-api/agents_api/__init__.py @@ -1,7 +1,11 @@ """ -The `agents_api` module serves as the foundation of the agent management system within the `agents-api` project. This module is responsible for defining the structure and capabilities of the API that manages agents. It includes operations such as creating, updating, listing, and deleting agents, as well as managing documents and tools associated with these agents. This file, in particular, acts as a configuration and initialization point for the `agents_api` package, and as per the directive, should not contain any imports. +The `agents_api` module serves as the foundation of the agent management system within the `agents-api` project. +This module is responsible for defining the structure and capabilities of the API that manages agents. +It includes operations such as creating, updating, listing, and deleting agents, as well as managing documents and tools associated with these agents. """ -################################# -## Do NOT put any imports here ## -################################# +# Failed decoding arguments fix +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + import msgpack as msgpack diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index 924424881..80765bb79 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,25 +1,41 @@ +import asyncio +import operator +from functools import reduce +from itertools import batched + from beartype import beartype from temporalio import activity from ..clients import cozo, litellm +from ..common.storage_handler import auto_blob_store from ..env import testing from ..models.docs.embed_snippets import embed_snippets as embed_snippets_query from .types import EmbedDocsPayload +@auto_blob_store(deep=True) @beartype -async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: +async def embed_docs( + payload: EmbedDocsPayload, cozo_client=None, max_batch_size: int = 100 +) -> None: indices, snippets = list(zip(*enumerate(payload.content))) + batched_snippets = batched(snippets, max_batch_size) embed_instruction: str = payload.embed_instruction or "" title: str = payload.title or "" - embeddings = await litellm.aembedding( - inputs=[ - ( - embed_instruction + (title + "\n\n" + snippet) if title else snippet - ).strip() - for snippet in snippets - ] + async def embed_batch(snippets): + return await litellm.aembedding( + inputs=[ + ( + embed_instruction + (title + "\n\n" + snippet) if title else snippet + ).strip() + for snippet in snippets + ] + ) + + embeddings = reduce( + operator.add, + await asyncio.gather(*[embed_batch(snippets) for snippets in batched_snippets]), ) embed_snippets_query( @@ -31,7 +47,9 @@ async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: ) -async def mock_embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: +async def mock_embed_docs( + payload: EmbedDocsPayload, cozo_client=None, max_batch_size=100 +) -> None: # Does nothing return None diff --git a/agents-api/agents_api/activities/excecute_api_call.py b/agents-api/agents_api/activities/excecute_api_call.py index 88fabce89..09a33aaa8 100644 --- a/agents-api/agents_api/activities/excecute_api_call.py +++ b/agents-api/agents_api/activities/excecute_api_call.py @@ -1,18 +1,14 @@ -from typing import Annotated, Any, Optional, TypedDict, Union +import base64 +from typing import Any, Optional, TypedDict, Union import httpx from beartype import beartype -from pydantic import Field from temporalio import activity from ..autogen.openapi_model import ApiCallDef - -# from ..clients import integrations -from ..common.protocol.tasks import StepContext +from ..common.storage_handler import auto_blob_store from ..env import testing -# from ..models.tools import get_tool_args_from_metadata - class RequestArgs(TypedDict): content: Optional[str] @@ -20,30 +16,44 @@ class RequestArgs(TypedDict): json_: Optional[dict[str, Any]] cookies: Optional[dict[str, str]] params: Optional[Union[str, dict[str, Any]]] + url: Optional[str] + headers: Optional[dict[str, str]] +@auto_blob_store(deep=True) @beartype async def execute_api_call( api_call: ApiCallDef, request_args: RequestArgs, ) -> Any: try: - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(timeout=600) as client: + arg_url = request_args.pop("url", None) + arg_headers = request_args.pop("headers", None) + response = await client.request( method=api_call.method, - url=str(api_call.url), - headers=api_call.headers, + url=arg_url or str(api_call.url), + headers={**(arg_headers or {}), **(api_call.headers or {})}, follow_redirects=api_call.follow_redirects, **request_args, ) + response.raise_for_status() + content_base64 = base64.b64encode(response.content).decode("ascii") + response_dict = { "status_code": response.status_code, "headers": dict(response.headers), - "content": response.content, - "json": response.json(), + "content": content_base64, } + try: + response_dict["json"] = response.json() + except BaseException as e: + response_dict["json"] = None + activity.logger.debug(f"Failed to parse JSON response: {e}") + return response_dict except BaseException as e: diff --git a/agents-api/agents_api/activities/execute_integration.py b/agents-api/agents_api/activities/execute_integration.py index 964ad6e12..e12bec5cc 100644 --- a/agents-api/agents_api/activities/execute_integration.py +++ b/agents-api/agents_api/activities/execute_integration.py @@ -3,18 +3,21 @@ from beartype import beartype from temporalio import activity -from ..autogen.openapi_model import IntegrationDef +from ..autogen.openapi_model import BaseIntegrationDef from ..clients import integrations +from ..common.exceptions.tools import IntegrationExecutionException from ..common.protocol.tasks import StepContext +from ..common.storage_handler import auto_blob_store from ..env import testing from ..models.tools import get_tool_args_from_metadata +@auto_blob_store(deep=True) @beartype async def execute_integration( context: StepContext, tool_name: str, - integration: IntegrationDef, + integration: BaseIntegrationDef, arguments: dict[str, Any], setup: dict[str, Any] = {}, ) -> Any: @@ -40,16 +43,32 @@ async def execute_integration( if integration.provider == "dummy": return arguments - return await integrations.run_integration_service( + integration_service_response = await integrations.run_integration_service( provider=integration.provider, setup=setup, method=integration.method, arguments=arguments, ) + if ( + "error" in integration_service_response + and integration_service_response["error"] + ): + raise IntegrationExecutionException( + integration=integration, + error=integration_service_response["error"], + ) + + return integration_service_response + except BaseException as e: if activity.in_activity(): - activity.logger.error(f"Error in execute_integration: {e}") + integration_str = integration.provider + ( + "." + integration.method if integration.method else "" + ) + activity.logger.error( + f"Error in execute_integration {integration_str}: {e}" + ) raise diff --git a/agents-api/agents_api/activities/execute_system.py b/agents-api/agents_api/activities/execute_system.py index 8e4d71274..b91c52e76 100644 --- a/agents-api/agents_api/activities/execute_system.py +++ b/agents-api/agents_api/activities/execute_system.py @@ -1,161 +1,168 @@ +import asyncio +from concurrent.futures import ProcessPoolExecutor +from functools import partial from typing import Any from uuid import UUID from beartype import beartype +from box import Box, BoxList +from fastapi.background import BackgroundTasks from temporalio import activity -from ..autogen.Tools import SystemDef +from ..autogen.openapi_model import ( + ChatInput, + CreateDocRequest, + CreateSessionRequest, + HybridDocSearchRequest, + SystemDef, + TextOnlyDocSearchRequest, + VectorDocSearchRequest, +) from ..common.protocol.tasks import StepContext +from ..common.storage_handler import auto_blob_store, load_from_blob_store_if_remote from ..env import testing -from ..models.agent.create_agent import create_agent as create_agent_query -from ..models.agent.delete_agent import delete_agent as delete_agent_query -from ..models.agent.get_agent import get_agent as get_agent_query -from ..models.agent.list_agents import list_agents as list_agents_query -from ..models.agent.update_agent import update_agent as update_agent_query -from ..models.docs.create_doc import create_doc as create_doc_query -from ..models.docs.delete_doc import delete_doc as delete_doc_query -from ..models.docs.get_doc import get_doc as get_doc_query -from ..models.docs.list_docs import list_docs as list_docs_query -from ..models.session.create_session import create_session as create_session_query -from ..models.session.delete_session import delete_session as delete_session_query -from ..models.session.get_session import get_session as get_session_query -from ..models.session.list_sessions import list_sessions as list_sessions_query -from ..models.session.update_session import update_session as update_session_query -from ..models.task.create_task import create_task as create_task_query -from ..models.task.delete_task import delete_task as delete_task_query -from ..models.task.get_task import get_task as get_task_query -from ..models.task.list_tasks import list_tasks as list_tasks_query -from ..models.task.update_task import update_task as update_task_query -from ..models.user.create_user import create_user as create_user_query -from ..models.user.delete_user import delete_user as delete_user_query -from ..models.user.get_user import get_user as get_user_query -from ..models.user.list_users import list_users as list_users_query -from ..models.user.update_user import update_user as update_user_query +from ..models.developer import get_developer +from .utils import get_handler + +# For running synchronous code in the background +process_pool_executor = ProcessPoolExecutor() +@auto_blob_store(deep=True) @beartype async def execute_system( context: StepContext, system: SystemDef, ) -> Any: - arguments = system.arguments + """Execute a system call with the appropriate handler and transformed arguments.""" + arguments: dict[str, Any] = system.arguments or {} + + if set(arguments.keys()) == {"bucket", "key"}: + arguments = await load_from_blob_store_if_remote(arguments) + arguments["developer_id"] = context.execution_input.developer_id + # Unbox all the arguments + for key, value in arguments.items(): + if isinstance(value, Box): + arguments[key] = value.to_dict() + elif isinstance(value, BoxList): + arguments[key] = value.to_list() + # Convert all UUIDs to UUID objects - if "agent_id" in arguments: - arguments["agent_id"] = UUID(arguments["agent_id"]) - if "user_id" in arguments: - arguments["user_id"] = UUID(arguments["user_id"]) - if "task_id" in arguments: - arguments["task_id"] = UUID(arguments["task_id"]) - if "session_id" in arguments: - arguments["session_id"] = UUID(arguments["session_id"]) - if "doc_id" in arguments: - arguments["doc_id"] = UUID(arguments["doc_id"]) - - # FIXME: This is a total mess. Should be refactored. + uuid_fields = ["agent_id", "user_id", "task_id", "session_id", "doc_id"] + for field in uuid_fields: + if field in arguments: + arguments[field] = UUID(arguments[field]) + try: - # AGENTS - if system.resource == "agent": - # DOCS SUBRESOURCE - if system.subresource == "doc": - # Define the arguments for the agent doc queries - agent_doc_args = { - **{ - "owner_type": "agent", - "owner_id": arguments.pop("agent_id"), - }, - **arguments, - } - if system.operation == "list": - return list_docs_query(**agent_doc_args) - elif system.operation == "create": - return create_doc_query(**agent_doc_args) - elif system.operation == "delete": - return delete_doc_query(**agent_doc_args) - - # NO SUBRESOURCE - elif system.subresource == None: - if system.operation == "list": - return list_agents_query(**arguments) - elif system.operation == "get": - return get_agent_query(**arguments) - elif system.operation == "create": - return create_agent_query(**arguments) - elif system.operation == "update": - return update_agent_query(**arguments) - elif system.operation == "delete": - return delete_agent_query(**arguments) - - # USERS - elif system.resource == "user": - # DOCS SUBRESOURCE - if system.subresource == "doc": - # Define the arguments for the user doc queries - user_doc_args = { - **{ - "owner_type": "user", - "owner_id": arguments.pop("user_id"), - }, + handler = get_handler(system) + + # Transform arguments for doc-related operations (except create and search + # as we're calling the endpoint function rather than the model method) + if system.subresource == "doc" and system.operation not in ["create", "search"]: + owner_id_field = f"{system.resource}_id" + if owner_id_field in arguments: + doc_args = { + "owner_type": system.resource, + "owner_id": arguments[owner_id_field], **arguments, } - if system.operation == "list": - return list_docs_query(**user_doc_args) - elif system.operation == "create": - return create_doc_query(**user_doc_args) - elif system.operation == "delete": - return delete_doc_query(**user_doc_args) - - # NO SUBRESOURCE - elif system.subresource == None: - if system.operation == "list": - return list_users_query(**arguments) - elif system.operation == "get": - return get_user_query(**arguments) - elif system.operation == "create": - return create_user_query(**arguments) - elif system.operation == "update": - return update_user_query(**arguments) - elif system.operation == "delete": - return delete_user_query(**arguments) - - # SESSIONS - elif system.resource == "session": - if system.operation == "list": - return list_sessions_query(**arguments) - elif system.operation == "get": - return get_session_query(**arguments) - elif system.operation == "create": - return create_session_query(**arguments) - elif system.operation == "update": - return update_session_query(**arguments) - elif system.operation == "delete": - return update_session_query(**arguments) - elif system.operation == "delete": - return delete_session_query(**arguments) - # TASKS - elif system.resource == "task": - if system.operation == "list": - return list_tasks_query(**arguments) - elif system.operation == "get": - return get_task_query(**arguments) - elif system.operation == "create": - return create_task_query(**arguments) - elif system.operation == "update": - return update_task_query(**arguments) - elif system.operation == "delete": - return delete_task_query(**arguments) - - raise NotImplementedError(f"System call not implemented for { - system.resource}.{system.operation}") + doc_args.pop(owner_id_field) + arguments = doc_args + # Handle special cases for doc operations + if system.operation == "create" and system.subresource == "doc": + arguments["x_developer_id"] = arguments.pop("developer_id") + bg_runner = BackgroundTasks() + res = await handler( + data=CreateDocRequest(**arguments.pop("data")), + background_tasks=bg_runner, + **arguments, + ) + await bg_runner() + return res + + # Handle search operations + if system.operation == "search" and system.subresource == "doc": + arguments["x_developer_id"] = arguments.pop("developer_id") + search_params = _create_search_request(arguments) + return await handler(search_params=search_params, **arguments) + + # Handle chat operations + if system.operation == "chat" and system.resource == "session": + developer = get_developer(developer_id=arguments.get("developer_id")) + session_id = arguments.get("session_id") + x_custom_api_key = arguments.get("x_custom_api_key", None) + chat_input = ChatInput(**arguments) + bg_runner = BackgroundTasks() + res = await handler( + developer=developer, + session_id=session_id, + background_tasks=bg_runner, + x_custom_api_key=x_custom_api_key, + chat_input=chat_input, + ) + await bg_runner() + return res + + if system.operation == "create" and system.resource == "session": + developer_id = arguments.pop("developer_id") + session_id = arguments.pop("session_id", None) + data = CreateSessionRequest(**arguments) + + # In case sessions.create becomes asynchronous in the future + if asyncio.iscoroutinefunction(handler): + return await handler() + + # Run the synchronous function in another process + loop = asyncio.get_running_loop() + return await loop.run_in_executor( + process_pool_executor, partial(handler, developer_id, session_id, data) + ) + + # Handle regular operations + if asyncio.iscoroutinefunction(handler): + return await handler(**arguments) + + # Run the synchronous function in another process + loop = asyncio.get_running_loop() + return await loop.run_in_executor( + process_pool_executor, partial(handler, **arguments) + ) except BaseException as e: if activity.in_activity(): activity.logger.error(f"Error in execute_system_call: {e}") raise -# Mock and activity definition +def _create_search_request(arguments: dict) -> Any: + """Create appropriate search request based on available parameters.""" + if "text" in arguments and "vector" in arguments: + return HybridDocSearchRequest( + text=arguments.pop("text"), + mmr_strength=arguments.pop("mmr_strength", 0), + vector=arguments.pop("vector"), + alpha=arguments.pop("alpha", 0.75), + confidence=arguments.pop("confidence", 0.5), + limit=arguments.get("limit", 10), + ) + elif "text" in arguments: + return TextOnlyDocSearchRequest( + text=arguments.pop("text"), + mmr_strength=arguments.pop("mmr_strength", 0), + limit=arguments.get("limit", 10), + ) + elif "vector" in arguments: + return VectorDocSearchRequest( + vector=arguments.pop("vector"), + mmr_strength=arguments.pop("mmr_strength", 0), + confidence=arguments.pop("confidence", 0.7), + limit=arguments.get("limit", 10), + ) + + +# Keep the existing mock and activity definition mock_execute_system = execute_system execute_system = activity.defn(name="execute_system")( diff --git a/agents-api/agents_api/activities/sync_items_remote.py b/agents-api/agents_api/activities/sync_items_remote.py new file mode 100644 index 000000000..d71a5c566 --- /dev/null +++ b/agents-api/agents_api/activities/sync_items_remote.py @@ -0,0 +1,29 @@ +import asyncio +from typing import Any + +from beartype import beartype +from temporalio import activity + +from ..common.protocol.remote import RemoteObject + + +@beartype +async def save_inputs_remote_fn(inputs: list[Any]) -> list[Any | RemoteObject]: + from ..common.storage_handler import store_in_blob_store_if_large + + return await asyncio.gather( + *[store_in_blob_store_if_large(input) for input in inputs] + ) + + +@beartype +async def load_inputs_remote_fn(inputs: list[Any | RemoteObject]) -> list[Any]: + from ..common.storage_handler import load_from_blob_store_if_remote + + return await asyncio.gather( + *[load_from_blob_store_if_remote(input) for input in inputs] + ) + + +save_inputs_remote = activity.defn(name="save_inputs_remote")(save_inputs_remote_fn) +load_inputs_remote = activity.defn(name="load_inputs_remote")(load_inputs_remote_fn) diff --git a/agents-api/agents_api/activities/task_steps/base_evaluate.py b/agents-api/agents_api/activities/task_steps/base_evaluate.py index 3fcbf2f73..d87b961d3 100644 --- a/agents-api/agents_api/activities/task_steps/base_evaluate.py +++ b/agents-api/agents_api/activities/task_steps/base_evaluate.py @@ -1,18 +1,72 @@ import ast from typing import Any +import simpleeval from beartype import beartype from box import Box from openai import BaseModel -from temporalio import activity -from ...env import testing -from ..utils import get_evaluator +# Increase the max string length to 2048000 +simpleeval.MAX_STRING_LENGTH = 2048000 + +from simpleeval import NameNotDefined, SimpleEval # noqa: E402 +from temporalio import activity # noqa: E402 +from thefuzz import fuzz # noqa: E402 + +from ...common.storage_handler import auto_blob_store # noqa: E402 +from ...env import testing # noqa: E402 +from ..utils import get_evaluator # noqa: E402 + + +class EvaluateError(Exception): + def __init__(self, error, expression, values): + error_message = error.message if hasattr(error, "message") else str(error) + message = error_message + + # Catch a possible jinja template error + if "unhashable" in error_message and "{{" in expression: + message += "\nSuggestion: It seems like you used a jinja template, did you mean to use a python expression?" + + # Catch a possible misspell in a variable name + if isinstance(error, NameNotDefined): + misspelledName = error_message.split("'")[1] + for variableName in values.keys(): + if fuzz.ratio(variableName, misspelledName) >= 90.0: + message += f"\nDid you mean '{variableName}' instead of '{misspelledName}'?" + super().__init__(message) + + +# Recursive evaluation helper function +def _recursive_evaluate(expr, evaluator: SimpleEval): + if isinstance(expr, str): + try: + return evaluator.eval(expr) + except Exception as e: + if activity.in_activity(): + evaluate_error = EvaluateError(e, expr, evaluator.names) + + variables_accessed = { + name: value + for name, value in evaluator.names.items() + if name in expr + } + + activity.logger.error( + f"Error in base_evaluate: {evaluate_error}\nVariables accessed: {variables_accessed}" + ) + raise evaluate_error from e + elif isinstance(expr, list): + return [_recursive_evaluate(e, evaluator) for e in expr] + elif isinstance(expr, dict): + return {k: _recursive_evaluate(v, evaluator) for k, v in expr.items()} + else: + raise ValueError(f"Invalid expression: {expr}") +@auto_blob_store(deep=True) @beartype async def base_evaluate( - exprs: str | list[str] | dict[str, str] | dict[str, dict[str, str]], + exprs: Any, values: dict[str, Any] = {}, extra_lambda_strs: dict[str, str] | None = None, ) -> Any | list[Any] | dict[str, Any]: @@ -43,33 +97,11 @@ async def base_evaluate( # frozen_box doesn't work coz we need some mutability in the values values = Box(values, frozen_box=False, conversion_box=True) - evaluator = get_evaluator(names=values, extra_functions=extra_lambdas) - - try: - match exprs: - case str(): - return evaluator.eval(exprs) - - case list(): - return [evaluator.eval(expr) for expr in exprs] - - case dict() as d if all(isinstance(v, dict) for v in d.values()): - return { - k: {ik: evaluator.eval(iv) for ik, iv in v.items()} - for k, v in d.items() - } - - case dict(): - return {k: evaluator.eval(v) for k, v in exprs.items()} - - case _: - raise ValueError(f"Invalid expression: {exprs}") - - except BaseException as e: - if activity.in_activity(): - activity.logger.error(f"Error in base_evaluate: {e}") + evaluator: SimpleEval = get_evaluator(names=values, extra_functions=extra_lambdas) - raise + # Recursively evaluate the expression + result = _recursive_evaluate(exprs, evaluator) + return result # Note: This is here just for clarity. We could have just imported base_evaluate directly diff --git a/agents-api/agents_api/activities/task_steps/cozo_query_step.py b/agents-api/agents_api/activities/task_steps/cozo_query_step.py index 8d28d83c9..16e9a53d8 100644 --- a/agents-api/agents_api/activities/task_steps/cozo_query_step.py +++ b/agents-api/agents_api/activities/task_steps/cozo_query_step.py @@ -4,9 +4,11 @@ from temporalio import activity from ... import models +from ...common.storage_handler import auto_blob_store from ...env import testing +@auto_blob_store(deep=True) @beartype async def cozo_query_step( query_name: str, diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 6b1650ff4..904ec3b9d 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -5,9 +5,11 @@ from ...activities.utils import simple_eval_dict from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...env import testing +@auto_blob_store(deep=True) @beartype async def evaluate_step( context: StepContext, @@ -21,7 +23,8 @@ async def evaluate_step( else context.current_step.evaluate ) - values = context.model_dump() | additional_values + values = await context.prepare_for_step(include_remote=True) | additional_values + output = simple_eval_dict(expr, values) result = StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/for_each_step.py b/agents-api/agents_api/activities/task_steps/for_each_step.py index 45f6d11dc..f51c1ef76 100644 --- a/agents-api/agents_api/activities/task_steps/for_each_step.py +++ b/agents-api/agents_api/activities/task_steps/for_each_step.py @@ -1,5 +1,3 @@ -import logging - from beartype import beartype from temporalio import activity @@ -8,22 +6,24 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) @beartype async def for_each_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, ForeachStep) output = await base_evaluate( - context.current_step.foreach.in_, context.model_dump() + context.current_step.foreach.in_, await context.prepare_for_step() ) return StepOutcome(output=output) except BaseException as e: - logging.error(f"Error in for_each_step: {e}") + activity.logger.error(f"Error in for_each_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/get_value_step.py b/agents-api/agents_api/activities/task_steps/get_value_step.py index cda00789e..ca38bc4fe 100644 --- a/agents-api/agents_api/activities/task_steps/get_value_step.py +++ b/agents-api/agents_api/activities/task_steps/get_value_step.py @@ -2,16 +2,18 @@ from temporalio import activity from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...env import testing # TODO: We should use this step to query the parent workflow and get the value from the workflow context # SCRUM-1 +@auto_blob_store(deep=True) @beartype async def get_value_step( context: StepContext, ) -> StepOutcome: - key: str = context.current_step.get + key: str = context.current_step.get # noqa: F841 raise NotImplementedError("Not implemented yet") diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index ecb935ca6..cf3764199 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -6,10 +6,12 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) @beartype async def if_else_step(context: StepContext) -> StepOutcome: # NOTE: This activity is only for logging, so we just evaluate the expression @@ -18,7 +20,7 @@ async def if_else_step(context: StepContext) -> StepOutcome: assert isinstance(context.current_step, IfElseWorkflowStep) expr: str = context.current_step.if_ - output = await base_evaluate(expr, context.model_dump()) + output = await base_evaluate(expr, await context.prepare_for_step()) output: bool = bool(output) result = StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index 34def9bf2..28fea2dae 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -6,10 +6,12 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...common.utils.template import render_template from ...env import testing +@auto_blob_store(deep=True) @beartype async def log_step(context: StepContext) -> StepOutcome: # NOTE: This activity is only for logging, so we just evaluate the expression @@ -19,7 +21,9 @@ async def log_step(context: StepContext) -> StepOutcome: template: str = context.current_step.log output = await render_template( - template, context.model_dump(), skip_vars=["developer_id"] + template, + await context.prepare_for_step(include_remote=True), + skip_vars=["developer_id"], ) result = StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/map_reduce_step.py b/agents-api/agents_api/activities/task_steps/map_reduce_step.py index 97fd0c154..872988bb4 100644 --- a/agents-api/agents_api/activities/task_steps/map_reduce_step.py +++ b/agents-api/agents_api/activities/task_steps/map_reduce_step.py @@ -8,16 +8,20 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) @beartype async def map_reduce_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, MapReduceStep) - output = await base_evaluate(context.current_step.over, context.model_dump()) + output = await base_evaluate( + context.current_step.over, await context.prepare_for_step() + ) return StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py index bdc8a5c6e..b9c0f0d78 100644 --- a/agents-api/agents_api/activities/task_steps/prompt_step.py +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -1,101 +1,243 @@ from beartype import beartype +from litellm.types.utils import ModelResponse from temporalio import activity from temporalio.exceptions import ApplicationError -from ...autogen.Tools import Tool +from ...autogen.openapi_model import Tool from ...clients import ( litellm, # We dont directly import `acompletion` so we can mock it ) from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...common.utils.template import render_template -from ...models.tools.list_tools import list_tools +from ...env import debug +from .base_evaluate import base_evaluate +COMPUTER_USE_BETA_FLAG = "computer-use-2024-10-22" -# FIXME: This shouldn't be here. -def format_agent_tool(tool: Tool) -> dict: - if tool.function: + +def format_tool(tool: Tool) -> dict: + if tool.type == "function": return { "type": "function", "function": { "name": tool.name, "description": tool.description, - "parameters": tool.function.parameters, + "parameters": tool.function and tool.function.parameters, }, } - # TODO: Add integration | system | api_call tool types - else: - return {} + + # For other tool types, we need to translate them to the OpenAI function tool format + formatted = { + "type": "function", + "function": {"name": tool.name, "description": tool.description}, + } + + # FIXME: Implement system tools + # if tool.type == "system": + # handler: Callable = get_handler_with_filtered_params(tool.system) + + # lc_tool: BaseTool = tool_decorator(handler) + + # json_schema: dict = lc_tool.get_input_jsonschema() + + # formatted["function"]["description"] = formatted["function"][ + # "description" + # ] or json_schema.get("description") + + # formatted["function"]["parameters"] = json_schema + + # # FIXME: Implement integration tools + # elif tool.type == "integration": + # raise NotImplementedError("Integration tools are not supported") + + # # FIXME: Implement API call tools + # elif tool.type == "api_call": + # raise NotImplementedError("API call tools are not supported") + + return formatted + + +EVAL_PROMPT_PREFIX = "$_ " @activity.defn +@auto_blob_store(deep=True) @beartype async def prompt_step(context: StepContext) -> StepOutcome: # Get context data prompt: str | list[dict] = context.current_step.model_dump()["prompt"] - context_data: dict = context.model_dump() + context_data: dict = await context.prepare_for_step(include_remote=True) + + # If the prompt is a string and starts with $_ then we need to evaluate it + should_evaluate_prompt = isinstance(prompt, str) and prompt.startswith( + EVAL_PROMPT_PREFIX + ) + + if should_evaluate_prompt: + prompt = await base_evaluate( + prompt[len(EVAL_PROMPT_PREFIX) :].strip(), context_data + ) + + if not isinstance(prompt, (str, list)): + raise ApplicationError( + "Invalid prompt expression, expected a string or list" + ) - # Render template messages - prompt = await render_template( - prompt, - context_data, - skip_vars=["developer_id"], + # Wrap the prompt in a list if it is not already + prompt = ( + prompt if isinstance(prompt, list) else [{"role": "user", "content": prompt}] ) + + # Render template messages if we didn't evaluate the prompt + if not should_evaluate_prompt: + # Render template messages + prompt = await render_template( + prompt, + context_data, + skip_vars=["developer_id"], + ) + # Get settings and run llm agent_default_settings: dict = ( context.execution_input.agent.default_settings.model_dump() if context.execution_input.agent.default_settings else {} ) + agent_model: str = ( context.execution_input.agent.model if context.execution_input.agent.model else "gpt-4o" ) - agent_tools = list_tools( - developer_id=context.execution_input.developer_id, - agent_id=context.execution_input.agent.id, - limit=128, # Max number of supported functions in OpenAI. See https://platform.openai.com/docs/api-reference/chat/create - offset=0, - sort_by="created_at", - direction="desc", + excluded_keys = [ + "prompt", + "kind_", + "label", + "unwrap", + "auto_run_tools", + "disable_cache", + "tools", + ] + + # Get passed settings + passed_settings: dict = context.current_step.model_dump( + exclude=excluded_keys, exclude_unset=True ) + passed_settings.update(passed_settings.pop("settings", {})) - # Format agent_tools for litellm - formatted_agent_tools = [ - format_agent_tool(tool) for tool in agent_tools if format_agent_tool(tool) - ] + if not passed_settings.get("tools"): + passed_settings.pop("tool_choice", None) - if context.current_step.settings: - passed_settings: dict = context.current_step.settings.model_dump( - exclude_unset=True - ) - else: - passed_settings: dict = {} + # Format tools for litellm + formatted_tools = [format_tool(tool) for tool in context.tools] - # Wrap the prompt in a list if it is not already - if isinstance(prompt, str): - prompt = [{"role": "user", "content": prompt}] + # Map tools to their original objects + tools_mapping: dict[str, Tool] = { + fmt_tool.get("name") or fmt_tool.get("function", {}).get("name"): orig_tool + for fmt_tool, orig_tool in zip(formatted_tools, context.tools) + } + + # Check if using Claude model and has specific tool types + is_claude_model = agent_model.lower().startswith("claude-3.5") + + # FIXME: Hack to make the computer use tools compatible with litellm + # Issue was: litellm expects type to be `computer_20241022` and spec to be + # `function` (see: https://docs.litellm.ai/docs/providers/anthropic#computer-tools) + # but we don't allow that (spec should match type). + formatted_tools = [] + for i, tool in enumerate(context.tools): + if tool.type == "computer_20241022" and tool.computer_20241022: + function = tool.computer_20241022 + tool = { + "type": tool.type, + "function": { + "name": tool.name, + "parameters": { + k: v + for k, v in function.model_dump().items() + if k not in ["name", "type"] + }, + }, + } + formatted_tools.append(tool) + # For non-Claude models, we don't need to send tools + # FIXME: Enable formatted_tools once format-tools PR is merged. + if not is_claude_model: + formatted_tools = None + + # HOTFIX: for groq calls, litellm expects tool_calls_id not to be in the messages + # FIXME: This is a temporary fix. We need to update the agent-api to use the new tool calling format + # FIXME: Enable formatted_tools once format-tools PR is merged. + is_groq_model = agent_model.lower().startswith("llama-3.1") + if is_groq_model: + prompt = [ + { + k: v + for k, v in message.items() + if k not in ["tool_calls", "tool_call_id", "user", "continue_", "name"] + } + for message in prompt + ] + # Use litellm for other models completion_data: dict = { "model": agent_model, - "tools": formatted_agent_tools or None, + "tools": formatted_tools or None, "messages": prompt, **agent_default_settings, **passed_settings, } - response = await litellm.acompletion( + extra_body = { + "cache": {"no-cache": debug or context.current_step.disable_cache}, + } + + response: ModelResponse = await litellm.acompletion( **completion_data, + extra_body=extra_body, ) if context.current_step.unwrap: - if response.choices[0].finish_reason == "tool_calls": + if len(response.choices) != 1: + raise ApplicationError("Only one choice is supported") + + choice = response.choices[0] + if choice.finish_reason == "tool_calls": raise ApplicationError("Tool calls cannot be unwrapped") - response = response.choices[0].message.content + return StepOutcome( + output=choice.message.content, + next=None, + ) + + # Re-convert tool-calls to integration/system calls if needed + response_as_dict = response.model_dump() + + for choice in response_as_dict["choices"]: + if choice["finish_reason"] == "tool_calls": + calls = choice["message"]["tool_calls"] + + for call in calls: + call_name = call["function"]["name"] + call_args = call["function"]["arguments"] + + original_tool = tools_mapping.get(call_name) + if not original_tool: + raise ApplicationError(f"Tool {call_name} not found") + + if original_tool.type == "function": + continue + + call.pop("function") + call["type"] = original_tool.type + call[original_tool.type] = { + "name": call_name, + "arguments": call_args, + } return StepOutcome( - output=response.model_dump() if hasattr(response, "model_dump") else response, + output=response_as_dict, next=None, ) diff --git a/agents-api/agents_api/activities/task_steps/raise_complete_async.py b/agents-api/agents_api/activities/task_steps/raise_complete_async.py index 54fd8a32c..640d6ae4e 100644 --- a/agents-api/agents_api/activities/task_steps/raise_complete_async.py +++ b/agents-api/agents_api/activities/task_steps/raise_complete_async.py @@ -1,17 +1,19 @@ import base64 +from typing import Any +from beartype import beartype from temporalio import activity from ...autogen.openapi_model import CreateTransitionRequest -from ...common.protocol.tasks import ( - StepContext, - StepOutcome, -) +from ...common.protocol.tasks import StepContext +from ...common.storage_handler import auto_blob_store from .transition_step import original_transition_step @activity.defn -async def raise_complete_async(context: StepContext, output: StepOutcome) -> None: +@auto_blob_store(deep=True) +@beartype +async def raise_complete_async(context: StepContext, output: Any) -> None: activity_info = activity.info() captured_token = base64.b64encode(activity_info.task_token).decode("ascii") diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index 1e272bab2..08ac20de4 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -1,3 +1,4 @@ +from beartype import beartype from temporalio import activity from ...autogen.openapi_model import ReturnStep @@ -5,18 +6,19 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) +@beartype async def return_step(context: StepContext) -> StepOutcome: - # NOTE: This activity is only for returning immediately, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail try: assert isinstance(context.current_step, ReturnStep) exprs: dict[str, str] = context.current_step.return_ - output = await base_evaluate(exprs, context.model_dump()) + output = await base_evaluate(exprs, await context.prepare_for_step()) result = StepOutcome(output=output) return result diff --git a/agents-api/agents_api/activities/task_steps/set_value_step.py b/agents-api/agents_api/activities/task_steps/set_value_step.py index 2a199b324..1c97b6551 100644 --- a/agents-api/agents_api/activities/task_steps/set_value_step.py +++ b/agents-api/agents_api/activities/task_steps/set_value_step.py @@ -5,11 +5,13 @@ from ...activities.utils import simple_eval_dict from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...env import testing # TODO: We should use this step to signal to the parent workflow and set the value on the workflow context # SCRUM-2 +@auto_blob_store(deep=True) @beartype async def set_value_step( context: StepContext, @@ -19,7 +21,7 @@ async def set_value_step( try: expr = override_expr if override_expr is not None else context.current_step.set - values = context.model_dump() | additional_values + values = await context.prepare_for_step() | additional_values output = simple_eval_dict(expr, values) result = StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/switch_step.py b/agents-api/agents_api/activities/task_steps/switch_step.py index b28150450..6a95e98d2 100644 --- a/agents-api/agents_api/activities/task_steps/switch_step.py +++ b/agents-api/agents_api/activities/task_steps/switch_step.py @@ -6,10 +6,12 @@ StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store from ...env import testing from ..utils import get_evaluator +@auto_blob_store(deep=True) @beartype async def switch_step(context: StepContext) -> StepOutcome: try: @@ -19,7 +21,7 @@ async def switch_step(context: StepContext) -> StepOutcome: output: int = -1 cases: list[str] = [c.case for c in context.current_step.switch] - evaluator = get_evaluator(names=context.model_dump()) + evaluator = get_evaluator(names=await context.prepare_for_step()) for i, case in enumerate(cases): result = evaluator.eval(case) @@ -36,8 +38,6 @@ async def switch_step(context: StepContext) -> StepOutcome: return StepOutcome(error=str(e)) -# Note: This is here just for clarity. We could have just imported switch_step directly -# They do the same thing, so we dont need to mock the switch_step function mock_switch_step = switch_step switch_step = activity.defn(name="switch_step")( diff --git a/agents-api/agents_api/activities/task_steps/tool_call_step.py b/agents-api/agents_api/activities/task_steps/tool_call_step.py index 8d5d2fc8c..5725a75d1 100644 --- a/agents-api/agents_api/activities/task_steps/tool_call_step.py +++ b/agents-api/agents_api/activities/task_steps/tool_call_step.py @@ -6,11 +6,12 @@ from temporalio.exceptions import ApplicationError from ...activities.task_steps.base_evaluate import base_evaluate -from ...autogen.openapi_model import TaskToolDef, Tool, ToolCallStep +from ...autogen.openapi_model import CreateToolRequest, Tool, ToolCallStep from ...common.protocol.tasks import ( StepContext, StepOutcome, ) +from ...common.storage_handler import auto_blob_store # FIXME: This shouldn't be here. @@ -24,7 +25,9 @@ def generate_call_id(): # FIXME: This shouldn't be here, and shouldn't be done this way. Should be refactored. -def construct_tool_call(tool: TaskToolDef, arguments: dict, call_id: str) -> dict: +def construct_tool_call( + tool: CreateToolRequest | Tool, arguments: dict, call_id: str +) -> dict: return { tool.type: { "arguments": arguments, @@ -32,10 +35,10 @@ def construct_tool_call(tool: TaskToolDef, arguments: dict, call_id: str) -> dic } if tool.type != "system" else { - "resource": tool.spec["resource"], - "operation": tool.spec["operation"], - "resource_id": tool.spec["resource_id"], - "subresource": tool.spec["subresource"], + "resource": tool.system and tool.system.resource, + "operation": tool.system and tool.system.operation, + "resource_id": tool.system and tool.system.resource_id, + "subresource": tool.system and tool.system.subresource, "arguments": arguments, }, "id": call_id, @@ -44,6 +47,7 @@ def construct_tool_call(tool: TaskToolDef, arguments: dict, call_id: str) -> dic @activity.defn +@auto_blob_store(deep=True) @beartype async def tool_call_step(context: StepContext) -> StepOutcome: assert isinstance(context.current_step, ToolCallStep) @@ -57,7 +61,7 @@ async def tool_call_step(context: StepContext) -> StepOutcome: raise ApplicationError(f"Tool {tool_name} not found in the toolset") arguments = await base_evaluate( - context.current_step.arguments, context.model_dump() + context.current_step.arguments, await context.prepare_for_step() ) call_id = generate_call_id() diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py index 04ce6e8fd..a32a72ef9 100644 --- a/agents-api/agents_api/activities/task_steps/transition_step.py +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -3,8 +3,11 @@ from ...autogen.openapi_model import CreateTransitionRequest, Transition from ...common.protocol.tasks import StepContext +from ...common.storage_handler import load_from_blob_store_if_remote from ...env import testing -from ...models.execution.create_execution_transition import create_execution_transition +from ...models.execution.create_execution_transition import ( + create_execution_transition_async, +) @beartype @@ -12,8 +15,13 @@ async def transition_step( context: StepContext, transition_info: CreateTransitionRequest, ) -> Transition: + # Load output from blob store if it is a remote object + transition_info.output = await load_from_blob_store_if_remote( + transition_info.output + ) + # Create transition - transition = create_execution_transition( + transition = await create_execution_transition_async( developer_id=context.execution_input.developer_id, execution_id=context.execution_input.execution.id, task_id=context.execution_input.task.id, diff --git a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py index 58b7da489..ad6eeb63e 100644 --- a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py +++ b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py @@ -1,17 +1,21 @@ +from beartype import beartype from temporalio import activity from ...autogen.openapi_model import WaitForInputStep from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) +@beartype async def wait_for_input_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, WaitForInputStep) exprs = context.current_step.wait_for_input.info - output = await base_evaluate(exprs, context.model_dump()) + output = await base_evaluate(exprs, await context.prepare_for_step()) result = StepOutcome(output=output) return result @@ -21,8 +25,6 @@ async def wait_for_input_step(context: StepContext) -> StepOutcome: return StepOutcome(error=str(e)) -# Note: This is here just for clarity. We could have just imported wait_for_input_step directly -# They do the same thing, so we dont need to mock the wait_for_input_step function mock_wait_for_input_step = wait_for_input_step wait_for_input_step = activity.defn(name="wait_for_input_step")( diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 41fa2eb87..9ec4d525c 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -3,17 +3,16 @@ from beartype import beartype from temporalio import activity -from agents_api.autogen.openapi_model import TransitionTarget, YieldStep - +from ...autogen.openapi_model import TransitionTarget, YieldStep from ...common.protocol.tasks import StepContext, StepOutcome +from ...common.storage_handler import auto_blob_store from ...env import testing from .base_evaluate import base_evaluate +@auto_blob_store(deep=True) @beartype async def yield_step(context: StepContext) -> StepOutcome: - # NOTE: This activity is only for returning immediately, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail try: assert isinstance(context.current_step, YieldStep) @@ -26,7 +25,7 @@ async def yield_step(context: StepContext) -> StepOutcome: ], f"Workflow {workflow} not found in task" # Evaluate the expressions in the arguments - arguments = await base_evaluate(exprs, context.model_dump()) + arguments = await base_evaluate(exprs, await context.prepare_for_step()) # Transition to the first step of that workflow transition_target = TransitionTarget( diff --git a/agents-api/agents_api/activities/utils.py b/agents-api/agents_api/activities/utils.py index f9f7ded12..3997104db 100644 --- a/agents-api/agents_api/activities/utils.py +++ b/agents-api/agents_api/activities/utils.py @@ -1,20 +1,32 @@ +import base64 +import datetime as dt +import functools +import itertools import json -from functools import reduce -from itertools import accumulate -from random import random -from time import time -from typing import Any, Callable +import math +import random +import statistics +import string +import time +import urllib.parse +from typing import Any, Callable, ParamSpec, TypeVar import re2 -import yaml +import zoneinfo from beartype import beartype from simpleeval import EvalWithCompoundTypes, SimpleEval -from yaml import CSafeLoader + +from ..autogen.openapi_model import SystemDef +from ..common.utils import yaml + +T = TypeVar("T") +R = TypeVar("R") +P = ParamSpec("P") + # TODO: We need to make sure that we dont expose any security issues ALLOWED_FUNCTIONS = { "abs": abs, - "accumulate": accumulate, "all": all, "any": any, "bool": bool, @@ -25,23 +37,178 @@ "int": int, "len": len, "list": list, - "load_json": json.loads, - "load_yaml": lambda string: yaml.load(string, Loader=CSafeLoader), "map": map, - "match_regex": lambda pattern, string: bool(re2.fullmatch(pattern, string)), "max": max, "min": min, - "random": random, "range": range, - "reduce": reduce, "round": round, - "search_regex": lambda pattern, string: re2.search(pattern, string), "set": set, "str": str, "sum": sum, - "time": time, "tuple": tuple, + "reduce": functools.reduce, "zip": zip, + "search_regex": lambda pattern, string: re2.search(pattern, string), + "load_json": json.loads, + "load_yaml": yaml.load, + "dump_json": json.dumps, + "dump_yaml": yaml.dump, + "match_regex": lambda pattern, string: bool(re2.fullmatch(pattern, string)), +} + + +class stdlib_re: + fullmatch = re2.fullmatch + search = re2.search + escape = re2.escape + findall = re2.findall + finditer = re2.finditer + match = re2.match + split = re2.split + sub = re2.sub + subn = re2.subn + + +class stdlib_json: + loads = json.loads + dumps = json.dumps + + +class stdlib_yaml: + load = yaml.load + dump = yaml.dump + + +class stdlib_time: + strftime = time.strftime + strptime = time.strptime + time = time + + +class stdlib_random: + choice = random.choice + choices = random.choices + sample = random.sample + shuffle = random.shuffle + randrange = random.randrange + randint = random.randint + random = random.random + + +class stdlib_itertools: + accumulate = itertools.accumulate + + +class stdlib_functools: + partial = functools.partial + reduce = functools.reduce + + +class stdlib_base64: + b64encode = base64.b64encode + b64decode = base64.b64decode + + +class stdlib_urllib: + class parse: + urlparse = urllib.parse.urlparse + urlencode = urllib.parse.urlencode + unquote = urllib.parse.unquote + quote = urllib.parse.quote + parse_qs = urllib.parse.parse_qs + parse_qsl = urllib.parse.parse_qsl + urlsplit = urllib.parse.urlsplit + urljoin = urllib.parse.urljoin + unwrap = urllib.parse.unwrap + + +class stdlib_string: + ascii_letters = string.ascii_letters + ascii_lowercase = string.ascii_lowercase + ascii_uppercase = string.ascii_uppercase + digits = string.digits + hexdigits = string.hexdigits + octdigits = string.octdigits + punctuation = string.punctuation + whitespace = string.whitespace + printable = string.printable + + +class stdlib_zoneinfo: + ZoneInfo = zoneinfo.ZoneInfo + + +class stdlib_datetime: + class timezone: + class utc: + utc = dt.timezone.utc + + class datetime: + now = dt.datetime.now + datetime = dt.datetime + timedelta = dt.timedelta + date = dt.date + time = dt.time + + timedelta = dt.timedelta + + +class stdlib_math: + sqrt = math.sqrt + exp = math.exp + ceil = math.ceil + floor = math.floor + isinf = math.isinf + isnan = math.isnan + log = math.log + log10 = math.log10 + log2 = math.log2 + pow = math.pow + sin = math.sin + cos = math.cos + tan = math.tan + asin = math.asin + acos = math.acos + atan = math.atan + atan2 = math.atan2 + + pi = math.pi + e = math.e + + +class stdlib_statistics: + mean = statistics.mean + stdev = statistics.stdev + geometric_mean = statistics.geometric_mean + median = statistics.median + median_low = statistics.median_low + median_high = statistics.median_high + mode = statistics.mode + quantiles = statistics.quantiles + + +stdlib = { + "re": stdlib_re, + "json": stdlib_json, + "yaml": stdlib_yaml, + "time": stdlib_time, + "random": stdlib_random, + "itertools": stdlib_itertools, + "functools": stdlib_functools, + "base64": stdlib_base64, + "urllib": stdlib_urllib, + "string": stdlib_string, + "zoneinfo": stdlib_zoneinfo, + "datetime": stdlib_datetime, + "math": stdlib_math, + "statistics": stdlib_statistics, +} + +constants = { + "NEWLINE": "\n", + "true": True, + "false": False, + "null": None, } @@ -50,7 +217,8 @@ def get_evaluator( names: dict[str, Any], extra_functions: dict[str, Callable] | None = None ) -> SimpleEval: evaluator = EvalWithCompoundTypes( - names=names, functions=ALLOWED_FUNCTIONS | (extra_functions or {}) + names=names | stdlib | constants, + functions=ALLOWED_FUNCTIONS | (extra_functions or {}), ) return evaluator @@ -61,3 +229,152 @@ def simple_eval_dict(exprs: dict[str, str], values: dict[str, Any]) -> dict[str, evaluator = get_evaluator(names=values) return {k: evaluator.eval(v) for k, v in exprs.items()} + + +def get_handler_with_filtered_params(system: SystemDef) -> Callable: + """ + Get the appropriate handler function based on the SystemDef. + + Parameters: + system (SystemDef): The system definition to get the handler for. + + Returns: + A wrapped handler function with problematic parameters filtered out + from its signature for JSON schema serialization. + """ + + from functools import wraps + from inspect import signature + + # Get the base handler based on system definition + base_handler = get_handler(system) + + # Skip parameters that can't be serialized to JSON schema + parameters_to_exclude = ["background_tasks"] + + # Get the original signature + sig = signature(base_handler) + + # Create a new function with filtered parameters + @wraps(base_handler) + def filtered_handler(*args, **kwargs): + return base_handler(*args, **kwargs) + + # Remove problematic parameters + filtered_handler.__signature__ = sig.replace( + parameters=[ + p for p in sig.parameters.values() if p.name not in parameters_to_exclude + ] + ) + + return filtered_handler + + +def get_handler(system: SystemDef) -> Callable: + """ + Internal function to get the base handler without parameter filtering. + + Parameters: + system (SystemDef): The system definition to get the handler for. + + Returns: + The base handler function. + """ + + from ..models.agent.create_agent import create_agent as create_agent_query + from ..models.agent.delete_agent import delete_agent as delete_agent_query + from ..models.agent.get_agent import get_agent as get_agent_query + from ..models.agent.list_agents import list_agents as list_agents_query + from ..models.agent.update_agent import update_agent as update_agent_query + from ..models.docs.delete_doc import delete_doc as delete_doc_query + from ..models.docs.list_docs import list_docs as list_docs_query + from ..models.session.create_session import create_session as create_session_query + from ..models.session.delete_session import delete_session as delete_session_query + from ..models.session.get_session import get_session as get_session_query + from ..models.session.list_sessions import list_sessions as list_sessions_query + from ..models.session.update_session import update_session as update_session_query + from ..models.task.create_task import create_task as create_task_query + from ..models.task.delete_task import delete_task as delete_task_query + from ..models.task.get_task import get_task as get_task_query + from ..models.task.list_tasks import list_tasks as list_tasks_query + from ..models.task.update_task import update_task as update_task_query + from ..models.user.create_user import create_user as create_user_query + from ..models.user.delete_user import delete_user as delete_user_query + from ..models.user.get_user import get_user as get_user_query + from ..models.user.list_users import list_users as list_users_query + from ..models.user.update_user import update_user as update_user_query + from ..routers.docs.create_doc import create_agent_doc, create_user_doc + from ..routers.docs.search_docs import search_agent_docs, search_user_docs + from ..routers.sessions.chat import chat + + match (system.resource, system.subresource, system.operation): + # AGENTS + case ("agent", "doc", "list"): + return list_docs_query + case ("agent", "doc", "create"): + return create_agent_doc + case ("agent", "doc", "delete"): + return delete_doc_query + case ("agent", "doc", "search"): + return search_agent_docs + case ("agent", None, "list"): + return list_agents_query + case ("agent", None, "get"): + return get_agent_query + case ("agent", None, "create"): + return create_agent_query + case ("agent", None, "update"): + return update_agent_query + case ("agent", None, "delete"): + return delete_agent_query + + # USERS + case ("user", "doc", "list"): + return list_docs_query + case ("user", "doc", "create"): + return create_user_doc + case ("user", "doc", "delete"): + return delete_doc_query + case ("user", "doc", "search"): + return search_user_docs + case ("user", None, "list"): + return list_users_query + case ("user", None, "get"): + return get_user_query + case ("user", None, "create"): + return create_user_query + case ("user", None, "update"): + return update_user_query + case ("user", None, "delete"): + return delete_user_query + + # SESSIONS + case ("session", None, "list"): + return list_sessions_query + case ("session", None, "get"): + return get_session_query + case ("session", None, "create"): + return create_session_query + case ("session", None, "update"): + return update_session_query + case ("session", None, "delete"): + return delete_session_query + case ("session", None, "chat"): + return chat + + # TASKS + case ("task", None, "list"): + return list_tasks_query + case ("task", None, "get"): + return get_task_query + case ("task", None, "create"): + return create_task_query + case ("task", None, "update"): + return update_task_query + case ("task", None, "delete"): + return delete_task_query + + case _: + raise NotImplementedError( + f"System call not implemented for {system.resource}.{system.operation}" + ) diff --git a/agents-api/agents_api/autogen/Agents.py b/agents-api/agents_api/autogen/Agents.py index 1b66b6a3c..5dab2c7b2 100644 --- a/agents-api/agents_api/autogen/Agents.py +++ b/agents-api/agents_api/autogen/Agents.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -28,11 +28,10 @@ class Agent(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the agent """ @@ -66,11 +65,10 @@ class CreateAgentRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the agent """ @@ -101,11 +99,10 @@ class CreateOrUpdateAgentRequest(CreateAgentRequest): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the agent """ @@ -139,11 +136,10 @@ class PatchAgentRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the agent """ @@ -177,11 +173,10 @@ class UpdateAgentRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the agent """ diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 27817568d..042f9164d 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -10,7 +10,14 @@ from .Common import LogitBias from .Docs import DocReference -from .Tools import ChosenToolCall, NamedToolChoice, Tool +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + CreateToolRequest, + NamedToolChoice, +) class BaseChatOutput(BaseModel): @@ -26,6 +33,18 @@ class BaseChatOutput(BaseModel): """ The log probabilities of tokens """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = None + """ + The tool calls generated by the model + """ class BaseChatResponse(BaseModel): @@ -36,11 +55,13 @@ class BaseChatResponse(BaseModel): """ Usage statistics for the completion request """ - jobs: Annotated[list[UUID], Field([], json_schema_extra={"readOnly": True})] + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] """ Background job IDs that may have been spawned from this interaction. """ - docs: Annotated[list[DocReference], Field([], json_schema_extra={"readOnly": True})] + docs: Annotated[ + list[DocReference], Field(json_schema_extra={"readOnly": True}) + ] = [] """ Documents referenced for this request (for citation purposes). """ @@ -71,7 +92,7 @@ class ChatInputData(BaseModel): """ A list of new input messages comprising the conversation so far. """ - tools: list[Tool] = [] + tools: list[CreateToolRequest] = [] """ (Advanced) List of tools that are provided in addition to agent's default set of tools. """ @@ -114,20 +135,20 @@ class CompetionUsage(BaseModel): populate_by_name=True, ) completion_tokens: Annotated[ - int | None, Field(None, json_schema_extra={"readOnly": True}) - ] + int | None, Field(json_schema_extra={"readOnly": True}) + ] = None """ Number of tokens in the generated completion """ prompt_tokens: Annotated[ - int | None, Field(None, json_schema_extra={"readOnly": True}) - ] + int | None, Field(json_schema_extra={"readOnly": True}) + ] = None """ Number of tokens in the prompt """ - total_tokens: Annotated[ - int | None, Field(None, json_schema_extra={"readOnly": True}) - ] + total_tokens: Annotated[int | None, Field(json_schema_extra={"readOnly": True})] = ( + None + ) """ Total number of tokens used in the request (prompt + completion) """ @@ -144,7 +165,107 @@ class Content(BaseModel): """ +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + +class ContentItemModel3(Content): + pass + + +class ContentItemModel4(ContentItemModel): + pass + + +class ContentItemModel5(Content): + pass + + +class ContentItemModel6(ContentItemModel): + pass + + class ContentModel(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel1(Content): + pass + + +class ContentModel2(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + +class ContentModel3(Content): + pass + + +class ContentModel4(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel3] | list[ContentItemModel4] + + +class ContentModel5(Content): + pass + + +class ContentModel6(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel5] | list[ContentItemModel6] + + +class ContentModel7(BaseModel): model_config = ConfigDict( populate_by_name=True, ) @@ -166,19 +287,15 @@ class Delta(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + role: Literal["user", "assistant", "system", "tool"] """ The role of the message """ - content: str | list[str] | list[Content | ContentModel] + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[ContentModel1 | ContentModel7 | ContentModel2] | None, + Field(...), + ] = None """ The content parts of the message """ @@ -186,10 +303,22 @@ class Delta(BaseModel): """ Name """ - continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None """ Whether to continue this message or return a new one """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ class ImageUrl(BaseModel): @@ -224,19 +353,15 @@ class Message(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + role: Literal["user", "assistant", "system", "tool"] """ The role of the message """ - content: str | list[str] | list[Content | ContentModel] + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[Content | ContentModel7 | ContentModel] | None, + Field(...), + ] = None """ The content parts of the message """ @@ -244,10 +369,22 @@ class Message(BaseModel): """ Name """ - continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None """ Whether to continue this message or return a new one """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ class MessageChatResponse(BaseChatResponse): @@ -264,19 +401,15 @@ class MessageModel(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + role: Literal["user", "assistant", "system", "tool"] """ The role of the message """ - content: str | list[str] | list[Content | ContentModel] + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[ContentModel3 | ContentModel7 | ContentModel4] | None, + Field(...), + ] = None """ The content parts of the message """ @@ -284,19 +417,25 @@ class MessageModel(BaseModel): """ Name """ - tool_calls: Annotated[ - list[ChosenToolCall] | None, Field([], json_schema_extra={"readOnly": True}) - ] + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] """ Tool calls generated by the model. """ created_at: Annotated[ - AwareDatetime | None, Field(None, json_schema_extra={"readOnly": True}) - ] + AwareDatetime | None, Field(json_schema_extra={"readOnly": True}) + ] = None """ When this resource was created as UTC date-time """ - id: Annotated[UUID | None, Field(None, json_schema_extra={"readOnly": True})] + id: Annotated[UUID | None, Field(json_schema_extra={"readOnly": True})] = None class MultipleChatOutput(BaseChatOutput): @@ -316,19 +455,19 @@ class OpenAISettings(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + frequency_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None """ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + presence_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None """ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] + temperature: Annotated[float | None, Field(ge=0.0, le=5.0)] = None """ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + top_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None """ Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ @@ -369,6 +508,15 @@ class SingleChatOutput(BaseChatOutput): message: MessageModel +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str + + class TokenLogProb(BaseTokenLogProb): model_config = ConfigDict( populate_by_name=True, @@ -386,7 +534,7 @@ class ChatInput(ChatInputData): model_config = ConfigDict( populate_by_name=True, ) - remember: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] + remember: Annotated[StrictBool, Field(json_schema_extra={"readOnly": True})] = False """ DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) """ @@ -401,11 +549,10 @@ class ChatInput(ChatInputData): model: Annotated[ str | None, Field( - None, max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = None """ Identifier of the model to be used """ @@ -413,15 +560,15 @@ class ChatInput(ChatInputData): """ Indicates if the server should stream the response as it's generated """ - stop: Annotated[list[str], Field([], max_length=4)] + stop: Annotated[list[str], Field(max_length=4)] = [] """ Up to 4 sequences where the API will stop generating further tokens. """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] + seed: Annotated[int | None, Field(ge=-1, le=1000)] = None """ If specified, the system will make a best effort to sample deterministically for that particular seed value """ - max_tokens: Annotated[int | None, Field(None, ge=1)] + max_tokens: Annotated[int | None, Field(ge=1)] = None """ The maximum number of tokens to generate in the chat completion """ @@ -439,31 +586,31 @@ class ChatInput(ChatInputData): """ Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) """ - repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + repetition_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + length_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. """ - min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + min_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None """ Minimum probability compared to leading token to be considered """ - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + frequency_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None """ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + presence_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None """ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] + temperature: Annotated[float | None, Field(ge=0.0, le=5.0)] = None """ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + top_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None """ Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ @@ -477,15 +624,15 @@ class DefaultChatSettings(OpenAISettings): model_config = ConfigDict( populate_by_name=True, ) - repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + repetition_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + length_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. """ - min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + min_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None """ Minimum probability compared to leading token to be considered """ @@ -498,11 +645,10 @@ class ChatSettings(DefaultChatSettings): model: Annotated[ str | None, Field( - None, max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = None """ Identifier of the model to be used """ @@ -510,15 +656,15 @@ class ChatSettings(DefaultChatSettings): """ Indicates if the server should stream the response as it's generated """ - stop: Annotated[list[str], Field([], max_length=4)] + stop: Annotated[list[str], Field(max_length=4)] = [] """ Up to 4 sequences where the API will stop generating further tokens. """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] + seed: Annotated[int | None, Field(ge=-1, le=1000)] = None """ If specified, the system will make a best effort to sample deterministically for that particular seed value """ - max_tokens: Annotated[int | None, Field(None, ge=1)] + max_tokens: Annotated[int | None, Field(ge=1)] = None """ The maximum number of tokens to generate in the chat completion """ diff --git a/agents-api/agents_api/autogen/Common.py b/agents-api/agents_api/autogen/Common.py index a2d6b99a6..888865ab7 100644 --- a/agents-api/agents_api/autogen/Common.py +++ b/agents-api/agents_api/autogen/Common.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -68,7 +68,7 @@ class ResourceCreatedResponse(BaseModel): """ When this resource was created as UTC date-time """ - jobs: Annotated[list[UUID], Field([], json_schema_extra={"readOnly": True})] + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] """ IDs (if any) of jobs created as part of this request """ @@ -86,7 +86,7 @@ class ResourceDeletedResponse(BaseModel): """ When this resource was deleted as UTC date-time """ - jobs: Annotated[list[UUID], Field([], json_schema_extra={"readOnly": True})] + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] """ IDs (if any) of jobs created as part of this request """ @@ -104,7 +104,7 @@ class ResourceUpdatedResponse(BaseModel): """ When this resource was updated as UTC date-time """ - jobs: Annotated[list[UUID], Field([], json_schema_extra={"readOnly": True})] + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] """ IDs (if any) of jobs created as part of this request """ diff --git a/agents-api/agents_api/autogen/Docs.py b/agents-api/agents_api/autogen/Docs.py index 0c1524e48..ffed27c1d 100644 --- a/agents-api/agents_api/autogen/Docs.py +++ b/agents-api/agents_api/autogen/Docs.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -13,11 +13,16 @@ class BaseDocSearchRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - limit: Annotated[int, Field(10, ge=1, le=100)] + limit: Annotated[int, Field(ge=1, le=50)] = 10 lang: Literal["en-US"] = "en-US" """ The language to be used for text-only search. Support for other languages coming soon. """ + metadata_filter: dict[str, Any] = {} + mmr_strength: Annotated[float, Field(ge=0.0, lt=1.0)] = 0 + """ + MMR Strength (mmr_strength = 1 - mmr_lambda) + """ class CreateDocRequest(BaseModel): @@ -37,6 +42,10 @@ class CreateDocRequest(BaseModel): """ Contents of the document """ + embed_instruction: str | None = None + """ + Instruction for the embedding model. + """ class Doc(BaseModel): @@ -59,8 +68,8 @@ class Doc(BaseModel): """ embeddings: Annotated[ list[float] | list[list[float]] | None, - Field(None, json_schema_extra={"readOnly": True}), - ] + Field(json_schema_extra={"readOnly": True}), + ] = None """ Embeddings for the document """ @@ -78,6 +87,7 @@ class DocReference(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + metadata: dict[str, Any] | None = None owner: DocOwner """ The owner of this document. @@ -87,7 +97,7 @@ class DocReference(BaseModel): ID of the document """ title: str | None = None - snippets: Annotated[list[Snippet], Field(min_length=1)] + snippet: Snippet distance: float | None = None @@ -105,16 +115,6 @@ class DocSearchResponse(BaseModel): """ -class EmbedQueryRequest(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - text: str | list[str] - """ - Text or texts to embed - """ - - class EmbedQueryResponse(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -129,11 +129,11 @@ class HybridDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) - confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] + confidence: Annotated[float, Field(ge=0.0, le=1.0)] = 0.5 """ The confidence cutoff level """ - alpha: Annotated[float, Field(0.75, ge=0.0, le=1.0)] + alpha: Annotated[float, Field(ge=0.0, le=1.0)] = 0.75 """ The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; """ @@ -147,12 +147,41 @@ class HybridDocSearchRequest(BaseDocSearchRequest): """ +class MultipleEmbedQueryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: Annotated[list[str], Field(max_length=100, min_length=1)] + """ + Texts to embed + """ + embed_instruction: str = "" + """ + Instruction for the embedding model. + """ + + +class SingleEmbedQueryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + """ + Text to embed + """ + embed_instruction: str = "" + """ + Instruction for the embedding model. + """ + + class Snippet(BaseModel): model_config = ConfigDict( populate_by_name=True, ) index: int content: str + embedding: list[float] | None = None class TextOnlyDocSearchRequest(BaseDocSearchRequest): @@ -169,7 +198,7 @@ class VectorDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) - confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] + confidence: Annotated[float, Field(ge=0.0, le=1.0)] = 0.5 """ The confidence cutoff level """ diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 1f4286eb9..de37e77d8 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -8,34 +8,43 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel -from .Tools import ChosenToolCall, Tool, ToolResponse +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + Tool, + ToolResponse, +) class BaseEntry(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + role: Literal["user", "assistant", "system", "tool"] """ - ChatML role (system|assistant|user|function_call|function|function_response|auto) + ChatML role (system|assistant|user|tool) """ name: str | None = None content: ( - list[Content | ContentModel] + list[Content | ContentModel3 | ContentModel] | Tool - | ChosenToolCall + | ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 | str | ToolResponse | list[ - list[Content | ContentModel] | Tool | ChosenToolCall | str | ToolResponse + list[ContentModel1 | ContentModel3 | ContentModel2] + | Tool + | ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + | str + | ToolResponse ] ) source: Literal[ @@ -43,39 +52,35 @@ class BaseEntry(BaseModel): ] tokenizer: str token_count: int + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = None + """ + Tool calls generated by the model. + """ + tool_call_id: str | None = None + """ + The tool call id of the tool call this message is a response to + """ timestamp: Annotated[float, Field(ge=0.0)] """ This is the time that this event refers to. """ -class ChatMLRole( - RootModel[ - Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] - ] -): +class ChatMLRole(RootModel[Literal["user", "assistant", "system", "tool"]]): model_config = ConfigDict( populate_by_name=True, ) - root: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + root: Literal["user", "assistant", "system", "tool"] """ - ChatML role (system|assistant|user|function_call|function|function_response|auto) + ChatML role (system|assistant|user|tool) """ @@ -90,7 +95,57 @@ class Content(BaseModel): """ +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + class ContentModel(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel1(Content): + pass + + +class ContentModel2(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + +class ContentModel3(BaseModel): model_config = ConfigDict( populate_by_name=True, ) @@ -163,3 +218,12 @@ class Relation(BaseModel): head: UUID relation: str tail: UUID + + +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index a4621c0ff..5ccc57e83 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations diff --git a/agents-api/agents_api/autogen/Files.py b/agents-api/agents_api/autogen/Files.py new file mode 100644 index 000000000..b7640f8bc --- /dev/null +++ b/agents-api/agents_api/autogen/Files.py @@ -0,0 +1,82 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + + +class CreateFileRequest(BaseModel): + """ + Payload for creating a file + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Name of the file + """ + description: str = "" + """ + Description of the file + """ + mime_type: str | None = None + """ + MIME type of the file + """ + content: str + """ + Base64 encoded content of the file + """ + + +class File(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Name of the file + """ + description: str = "" + """ + Description of the file + """ + mime_type: str | None = None + """ + MIME type of the file + """ + content: str + """ + Base64 encoded content of the file + """ + size: Annotated[int, Field(ge=1, json_schema_extra={"readOnly": True})] + """ + Size of the file in bytes + """ + hash: Annotated[str, Field(json_schema_extra={"readOnly": True})] + """ + Hash of the file + """ diff --git a/agents-api/agents_api/autogen/Jobs.py b/agents-api/agents_api/autogen/Jobs.py index 568b7a09c..b17cd9317 100644 --- a/agents-api/agents_api/autogen/Jobs.py +++ b/agents-api/agents_api/autogen/Jobs.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -25,11 +25,10 @@ class JobStatus(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the job """ @@ -41,7 +40,7 @@ class JobStatus(BaseModel): """ Whether this Job supports progress updates """ - progress: Annotated[float, Field(0, ge=0.0, le=100.0)] + progress: Annotated[float, Field(ge=0.0, le=100.0)] = 0 """ Progress percentage """ diff --git a/agents-api/agents_api/autogen/Sessions.py b/agents-api/agents_api/autogen/Sessions.py index 75e5252cd..3179c5c93 100644 --- a/agents-api/agents_api/autogen/Sessions.py +++ b/agents-api/agents_api/autogen/Sessions.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -27,7 +27,7 @@ class CreateSessionRequest(BaseModel): Agent ID of agent associated with this session """ agents: list[UUID] | None = None - situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ @@ -43,12 +43,10 @@ class CreateSessionRequest(BaseModel): """ Action to start on context window overflow """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = False """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. @@ -64,7 +62,7 @@ class PatchSessionRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ @@ -80,12 +78,10 @@ class PatchSessionRequest(BaseModel): """ Action to start on context window overflow """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = False """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. @@ -97,11 +93,11 @@ class Session(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ - summary: Annotated[str | None, Field(None, json_schema_extra={"readOnly": True})] + summary: Annotated[str | None, Field(json_schema_extra={"readOnly": True})] = None """ Summary (null at the beginning) - generated automatically after every interaction """ @@ -117,12 +113,10 @@ class Session(BaseModel): """ Action to start on context window overflow """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = False """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. @@ -174,7 +168,7 @@ class UpdateSessionRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ @@ -190,12 +184,10 @@ class UpdateSessionRequest(BaseModel): """ Action to start on context window overflow """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = False """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. @@ -218,7 +210,7 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest): Agent ID of agent associated with this session """ agents: list[UUID] | None = None - situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ @@ -234,12 +226,10 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest): """ Action to start on context window overflow """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = False """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index c7067ea0c..b9212d8cb 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -9,7 +9,14 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool from .Chat import ChatSettings -from .Tools import CreateToolRequest, NamedToolChoice +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + CreateToolRequest, + NamedToolChoice, +) class CaseThen(BaseModel): @@ -78,6 +85,26 @@ class Content(BaseModel): """ +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + class ContentModel(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -92,14 +119,40 @@ class ContentModel(BaseModel): """ -class ContentModel1(Content): +class ContentModel1(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel2(Content): pass -class ContentModel2(ContentModel): +class ContentModel3(ContentModel): pass +class ContentModel4(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + class CreateTaskRequest(BaseModel): """ Payload for creating a task @@ -142,9 +195,9 @@ class CreateTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: StrictBool = True + inherit_tools: StrictBool = False """ - Whether to inherit tools from the parent agent or not. Defaults to true. + Whether to inherit tools from the parent agent or not. Defaults to false. """ metadata: dict[str, Any] | None = None @@ -153,12 +206,16 @@ class ErrorWorkflowStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["error"], Field("error", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["error"], Field(json_schema_extra={"readOnly": True})] = ( + "error" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ error: str """ The error message @@ -170,12 +227,16 @@ class EvaluateStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["evaluate"], Field("evaluate", json_schema_extra={"readOnly": True}) - ] + Literal["evaluate"], Field(json_schema_extra={"readOnly": True}) + ] = "evaluate" """ The kind of step """ - evaluate: dict[str, str] + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + evaluate: dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] """ The expression to evaluate """ @@ -191,7 +252,8 @@ class ForeachDo(BaseModel): VALIDATION: Should NOT return more than 1000 elements. """ do: ( - EvaluateStep + WaitForInputStep + | EvaluateStep | ToolCallStep | PromptStep | GetStep @@ -214,7 +276,8 @@ class ForeachDoUpdateItem(BaseModel): VALIDATION: Should NOT return more than 1000 elements. """ do: ( - EvaluateStep + WaitForInputStep + | EvaluateStep | ToolCallStep | PromptStepUpdateItem | GetStep @@ -232,11 +295,15 @@ class ForeachStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["foreach"], Field("foreach", json_schema_extra={"readOnly": True}) - ] + Literal["foreach"], Field(json_schema_extra={"readOnly": True}) + ] = "foreach" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ foreach: ForeachDo """ The steps to run for each iteration @@ -247,6 +314,10 @@ class ForeachStepUpdateItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -261,10 +332,16 @@ class GetStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[Literal["get"], Field("get", json_schema_extra={"readOnly": True})] + kind_: Annotated[Literal["get"], Field(json_schema_extra={"readOnly": True})] = ( + "get" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ get: str """ The key to get @@ -276,11 +353,15 @@ class IfElseWorkflowStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["if_else"], Field("if_else", json_schema_extra={"readOnly": True}) - ] + Literal["if_else"], Field(json_schema_extra={"readOnly": True}) + ] = "if_else" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ if_: Annotated[str, Field(alias="if")] """ The condition to evaluate @@ -314,8 +395,8 @@ class IfElseWorkflowStep(BaseModel): | ErrorWorkflowStep | WaitForInputStep | None, - Field(None, alias="else"), - ] + Field(alias="else"), + ] = None """ The steps to run if the condition is false """ @@ -325,6 +406,10 @@ class IfElseWorkflowStepUpdateItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -362,8 +447,8 @@ class IfElseWorkflowStepUpdateItem(BaseModel): | ErrorWorkflowStep | WaitForInputStep | None, - Field(None, alias="else"), - ] + Field(alias="else"), + ] = None """ The steps to run if the condition is false """ @@ -391,10 +476,16 @@ class LogStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[Literal["log"], Field("log", json_schema_extra={"readOnly": True})] + kind_: Annotated[Literal["log"], Field(json_schema_extra={"readOnly": True})] = ( + "log" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ log: str """ The value to log @@ -406,11 +497,15 @@ class Main(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["map_reduce"], Field("map_reduce", json_schema_extra={"readOnly": True}) - ] + Literal["map_reduce"], Field(json_schema_extra={"readOnly": True}) + ] = "map_reduce" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ over: str """ The variable to iterate over @@ -437,7 +532,7 @@ class Main(BaseModel): """ The initial value of the reduce expression """ - parallelism: Annotated[int | None, Field(None, ge=1, le=100)] + parallelism: Annotated[int | None, Field(ge=1, le=100)] = None """ Whether to run the reduce expression in parallel and how many items to run in each batch """ @@ -447,6 +542,10 @@ class MainModel(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -477,7 +576,7 @@ class MainModel(BaseModel): """ The initial value of the reduce expression """ - parallelism: Annotated[int | None, Field(None, ge=1, le=100)] + parallelism: Annotated[int | None, Field(ge=1, le=100)] = None """ Whether to run the reduce expression in parallel and how many items to run in each batch """ @@ -488,11 +587,15 @@ class ParallelStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["parallel"], Field("parallel", json_schema_extra={"readOnly": True}) - ] + Literal["parallel"], Field(json_schema_extra={"readOnly": True}) + ] = "parallel" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ parallel: Annotated[ list[ EvaluateStep @@ -514,6 +617,10 @@ class ParallelStepUpdateItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -564,8 +671,8 @@ class PatchTaskRequest(BaseModel): | MainModel ] | None, - Field(None, min_length=1), - ] + Field(min_length=1), + ] = None """ The entrypoint of the task. """ @@ -577,9 +684,9 @@ class PatchTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: StrictBool = True + inherit_tools: StrictBool = False """ - Whether to inherit tools from the parent agent or not. Defaults to true. + Whether to inherit tools from the parent agent or not. Defaults to false. """ metadata: dict[str, Any] | None = None @@ -588,19 +695,15 @@ class PromptItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] + role: Literal["user", "assistant", "system", "tool"] """ The role of the message """ - content: list[str] | list[Content | ContentModel] | str + tool_call_id: str | None = None + content: Annotated[ + list[str] | list[Content | ContentModel | ContentModel1] | str | None, + Field(...), + ] """ The content parts of the message """ @@ -608,27 +711,43 @@ class PromptItem(BaseModel): """ Name """ - continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None """ Whether to continue this message or return a new one """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ class PromptStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["prompt"], Field("prompt", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["prompt"], Field(json_schema_extra={"readOnly": True})] = ( + "prompt" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ prompt: list[PromptItem] | str """ The prompt to run """ - tools: Literal["all"] | list[ToolRef | CreateToolRequest] = [] + tools: Literal["all"] | list[ToolRef | CreateToolRequest] = "all" """ The tools to use for the prompt """ @@ -644,22 +763,28 @@ class PromptStep(BaseModel): """ Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = True """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) If a tool call is made, the tool's output will be used as the model's input. If a tool call is not made, the model's output will be used as the next step's input. """ + disable_cache: StrictBool = False + """ + Whether to disable caching for the prompt step + """ class PromptStepUpdateItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -668,7 +793,7 @@ class PromptStepUpdateItem(BaseModel): """ The prompt to run """ - tools: Literal["all"] | list[ToolRefUpdateItem | CreateToolRequest] = [] + tools: Literal["all"] | list[ToolRefUpdateItem | CreateToolRequest] = "all" """ The tools to use for the prompt """ @@ -684,29 +809,38 @@ class PromptStepUpdateItem(BaseModel): """ Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` """ - forward_tool_results: StrictBool | None = None + auto_run_tools: StrictBool = True """ - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) If a tool call is made, the tool's output will be used as the model's input. If a tool call is not made, the model's output will be used as the next step's input. """ + disable_cache: StrictBool = False + """ + Whether to disable caching for the prompt step + """ class ReturnStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["return"], Field("return", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["return"], Field(json_schema_extra={"readOnly": True})] = ( + "return" + ) """ The kind of step """ - return_: Annotated[dict[str, str], Field(alias="return")] + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + return_: Annotated[ + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str], + Field(alias="return"), + ] """ The value to return """ @@ -716,10 +850,16 @@ class SetStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[Literal["set"], Field("set", json_schema_extra={"readOnly": True})] + kind_: Annotated[Literal["set"], Field(json_schema_extra={"readOnly": True})] = ( + "set" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ set: dict[str, str] """ The value to set @@ -730,19 +870,19 @@ class SleepFor(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - seconds: Annotated[int, Field(0, ge=0, le=60)] + seconds: Annotated[int, Field(ge=0, le=60)] = 0 """ The number of seconds to sleep for """ - minutes: Annotated[int, Field(0, ge=0, le=60)] + minutes: Annotated[int, Field(ge=0, le=60)] = 0 """ The number of minutes to sleep for """ - hours: Annotated[int, Field(0, ge=0, le=24)] + hours: Annotated[int, Field(ge=0, le=24)] = 0 """ The number of hours to sleep for """ - days: Annotated[int, Field(0, ge=0, le=30)] + days: Annotated[int, Field(ge=0, le=30)] = 0 """ The number of days to sleep for """ @@ -752,28 +892,48 @@ class SleepStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["sleep"], Field("sleep", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["sleep"], Field(json_schema_extra={"readOnly": True})] = ( + "sleep" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ sleep: SleepFor """ The duration to sleep for (max 31 days) """ +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str + """ + A valid jinja template. + """ + + class SwitchStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["switch"], Field("switch", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["switch"], Field(json_schema_extra={"readOnly": True})] = ( + "switch" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ switch: Annotated[list[CaseThen], Field(min_length=1)] """ The cond tree @@ -784,6 +944,10 @@ class SwitchStepUpdateItem(BaseModel): model_config = ConfigDict( populate_by_name=True, ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ kind_: str | None = None """ Discriminator property for BaseWorkflowStep. @@ -836,9 +1000,9 @@ class Task(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: StrictBool = True + inherit_tools: StrictBool = False """ - Whether to inherit tools from the parent agent or not. Defaults to true. + Whether to inherit tools from the parent agent or not. Defaults to false. """ id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] @@ -856,7 +1020,9 @@ class TaskTool(CreateToolRequest): model_config = ConfigDict( populate_by_name=True, ) - inherited: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] + inherited: Annotated[StrictBool, Field(json_schema_extra={"readOnly": True})] = ( + False + ) """ Read-only: Whether the tool was inherited or not. Only applies within tasks. """ @@ -867,16 +1033,38 @@ class ToolCallStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["tool_call"], Field("tool_call", json_schema_extra={"readOnly": True}) - ] + Literal["tool_call"], Field(json_schema_extra={"readOnly": True}) + ] = "tool_call" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ tool: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ The tool to run """ - arguments: dict[str, dict[str, str] | str] | Literal["_"] = "_" + arguments: ( + dict[ + str, + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | list[dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str]] + | str, + ] + | list[ + dict[ + str, + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | list[ + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + ] + | str, + ] + ] + | Literal["_"] + ) = "_" """ The input parameters for the tool (defaults to last step output) """ @@ -901,7 +1089,7 @@ class ToolRefById(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - id: UUID | None = None + id: str | None = None class ToolRefByName(BaseModel): @@ -912,7 +1100,7 @@ class ToolRefByName(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str | None, Field(None, max_length=40, pattern="^[^\\W0-9]\\w*$")] + name: Annotated[str | None, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] = None """ Valid python identifier names """ @@ -969,9 +1157,9 @@ class UpdateTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: StrictBool = True + inherit_tools: StrictBool = False """ - Whether to inherit tools from the parent agent or not. Defaults to true. + Whether to inherit tools from the parent agent or not. Defaults to false. """ metadata: dict[str, Any] | None = None @@ -980,7 +1168,7 @@ class WaitForInputInfo(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - info: dict[str, str] + info: dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] """ Any additional info or data """ @@ -991,12 +1179,15 @@ class WaitForInputStep(BaseModel): populate_by_name=True, ) kind_: Annotated[ - Literal["wait_for_input"], - Field("wait_for_input", json_schema_extra={"readOnly": True}), - ] + Literal["wait_for_input"], Field(json_schema_extra={"readOnly": True}) + ] = "wait_for_input" """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ wait_for_input: WaitForInputInfo """ Any additional info or data @@ -1007,18 +1198,25 @@ class YieldStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Annotated[ - Literal["yield"], Field("yield", json_schema_extra={"readOnly": True}) - ] + kind_: Annotated[Literal["yield"], Field(json_schema_extra={"readOnly": True})] = ( + "yield" + ) """ The kind of step """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ workflow: str """ The subworkflow to run. VALIDATION: Should resolve to a defined subworkflow. """ - arguments: dict[str, str] | Literal["_"] = "_" + arguments: ( + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | Literal["_"] + ) = "_" """ The input parameters for the subworkflow (defaults to last step output) """ diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index 423896e89..5d077f7b6 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -1,12 +1,19 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations from typing import Annotated, Any, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, StrictBool +from pydantic import ( + AnyUrl, + AwareDatetime, + BaseModel, + ConfigDict, + Field, + StrictBool, +) class ApiCallDef(BaseModel): @@ -27,6 +34,10 @@ class ApiCallDef(BaseModel): """ The URL to call """ + schema_: Annotated[dict[str, Any] | None, Field(alias="schema")] = None + """ + The schema of the response + """ headers: dict[str, str] | None = None """ The headers to send with the request @@ -39,7 +50,11 @@ class ApiCallDef(BaseModel): """ The data to send as form data """ - json_: Annotated[dict[str, Any] | None, Field(None, alias="json")] + files: dict[str, Any] | None = None + """ + The data to send as files data + """ + json_: Annotated[dict[str, Any] | None, Field(alias="json")] = None """ JSON body to send with the request """ @@ -90,6 +105,10 @@ class ApiCallDefUpdate(BaseModel): """ The URL to call """ + schema_: Annotated[dict[str, Any] | None, Field(alias="schema")] = None + """ + The schema of the response + """ headers: dict[str, str] | None = None """ The headers to send with the request @@ -102,7 +121,11 @@ class ApiCallDefUpdate(BaseModel): """ The data to send as form data """ - json_: Annotated[dict[str, Any] | None, Field(None, alias="json")] + files: dict[str, Any] | None = None + """ + The data to send as files data + """ + json_: Annotated[dict[str, Any] | None, Field(alias="json")] = None """ JSON body to send with the request """ @@ -124,89 +147,108 @@ class ApiCallDefUpdate(BaseModel): """ -class ChosenToolCall(BaseModel): +class ArxivSearchArguments(BaseModel): """ - The response tool value generated by the model + Arguments for Arxiv Search """ model_config = ConfigDict( populate_by_name=True, ) - type: Literal["function", "integration", "system", "api_call"] + query: str """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + The search query for searching with Arxiv + """ + id_list: list[str] | None = None + """ + The list of Arxiv IDs to search with + """ + max_results: Annotated[int, Field(ge=1, le=300000)] = 5 + """ + The maximum number of results to return + """ + download_pdf: StrictBool = False + """ + The download the pdf of the results + """ + sort_by: Literal["relevance", "lastUpdatedDate", "submittedDate"] = "relevance" + """ + The sort criterion for the results + """ + sort_order: Literal["ascending", "descending"] = "descending" + """ + The sort order for the results """ - function: FunctionCallOption | None = None - id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] -class CreateToolRequest(BaseModel): +class ArxivSearchArgumentsUpdate(BaseModel): """ - Payload for creating a tool + Arguments for Arxiv Search """ model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] - """ - Name of the tool (must be unique for this agent and a valid python identifier string ) - """ - description: str | None = None + query: str | None = None """ - Description of the tool + The search query for searching with Arxiv """ - function: FunctionDef | None = None + id_list: list[str] | None = None """ - The function to call + The list of Arxiv IDs to search with """ - integration: IntegrationDef | None = None + max_results: Annotated[int, Field(ge=1, le=300000)] = 5 """ - The integration to call + The maximum number of results to return """ - system: SystemDef | None = None + download_pdf: StrictBool = False """ - The system to call + The download the pdf of the results """ - api_call: ApiCallDef | None = None + sort_by: Literal["relevance", "lastUpdatedDate", "submittedDate"] = "relevance" """ - The API call to make + The sort criterion for the results """ - - -class FunctionCallOption(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - name: str + sort_order: Literal["ascending", "descending"] = "descending" """ - The name of the function + The sort order for the results """ -class FunctionDef(BaseModel): +class BaseChosenToolCall(BaseModel): """ - Function definition + The response tool value generated by the model """ model_config = ConfigDict( populate_by_name=True, ) - name: Any | None = None - """ - DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. - """ - description: Any | None = None + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] """ - DO NOT USE: This will be overriden by the tool description. Here only for compatibility reasons. + Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) """ - parameters: dict[str, Any] | None = None + function: FunctionCallOption | None = None + integration: Any | None = None + system: Any | None = None + api_call: Any | None = None + computer_20241022: ChosenComputer20241022 | None = None """ - The parameters the function accepts + (Alpha) Anthropic new tools """ + text_editor_20241022: ChosenTextEditor20241022 | None = None + bash_20241022: ChosenBash20241022 | None = None + id: Annotated[str | None, Field(json_schema_extra={"readOnly": True})] = None -class IntegrationDef(BaseModel): +class BaseIntegrationDef(BaseModel): """ Integration definition """ @@ -214,19 +256,20 @@ class IntegrationDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - provider: ( - Literal[ - "dummy", - "hacker_news", - "weather", - "wikipedia", - "spider", - "brave", - "browserbase", - "email", - ] - | str - ) + provider: Literal[ + "dummy", + "weather", + "wikipedia", + "spider", + "brave", + "browserbase", + "email", + "remote_browser", + "llama_parse", + "ffmpeg", + "cloudinary", + "arxiv", + ] """ The provider of the integration """ @@ -234,17 +277,17 @@ class IntegrationDef(BaseModel): """ The specific method of the integration to call """ - setup: dict[str, Any] | None = None + setup: Any | None = None """ The setup parameters the integration accepts """ - arguments: dict[str, Any] | None = None + arguments: Any | None = None """ The arguments to pre-apply to the integration call """ -class IntegrationDefUpdate(BaseModel): +class BaseIntegrationDefUpdate(BaseModel): """ Integration definition """ @@ -255,15 +298,18 @@ class IntegrationDefUpdate(BaseModel): provider: ( Literal[ "dummy", - "hacker_news", "weather", "wikipedia", "spider", "brave", "browserbase", "email", + "remote_browser", + "llama_parse", + "ffmpeg", + "cloudinary", + "arxiv", ] - | str | None ) = None """ @@ -273,233 +319,369 @@ class IntegrationDefUpdate(BaseModel): """ The specific method of the integration to call """ - setup: dict[str, Any] | None = None + setup: Any | None = None """ The setup parameters the integration accepts """ - arguments: dict[str, Any] | None = None + arguments: Any | None = None """ The arguments to pre-apply to the integration call """ -class NamedToolChoice(BaseModel): +class Bash20241022Def(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - function: FunctionCallOption | None = None + type: Literal["bash_20241022"] = "bash_20241022" + name: str = "bash" -class PatchToolRequest(BaseModel): +class Bash20241022DefUpdate(Bash20241022Def): + pass + + +class BraveIntegrationDef(BaseIntegrationDef): """ - Payload for patching a tool + Brave integration definition """ model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str | None, Field(None, max_length=40, pattern="^[^\\W0-9]\\w*$")] - """ - Name of the tool (must be unique for this agent and a valid python identifier string ) - """ - description: str | None = None - """ - Description of the tool - """ - function: FunctionDef | None = None + provider: Literal["brave"] = "brave" """ - The function to call + The provider must be "brave" """ - integration: IntegrationDefUpdate | None = None + method: str | None = None """ - The integration to call + The specific method of the integration to call """ - system: SystemDefUpdate | None = None + setup: BraveSearchSetup | None = None """ - The system to call + The setup parameters for Brave """ - api_call: ApiCallDefUpdate | None = None + arguments: BraveSearchArguments | None = None """ - The API call to make + The arguments for Brave Search """ -class SystemDef(BaseModel): +class BraveIntegrationDefUpdate(BaseIntegrationDefUpdate): """ - System definition + Brave integration definition """ model_config = ConfigDict( populate_by_name=True, ) - resource: Literal["agent", "user", "task", "execution", "doc", "session", "job"] + provider: Literal["brave"] = "brave" """ - Resource is the name of the resource to use + The provider must be "brave" """ - operation: Literal[ - "create", - "update", - "patch", - "create_or_update", - "embed", - "change_status", - "search", - "chat", - "history", - "delete", - "get", - "list", - ] + method: str | None = None """ - Operation is the name of the operation to perform + The specific method of the integration to call """ - resource_id: UUID | None = None + setup: BraveSearchSetupUpdate | None = None """ - Resource id (if applicable) + The setup parameters for Brave """ - subresource: Literal["tool", "doc", "execution", "transition"] | None = None + arguments: BraveSearchArgumentsUpdate | None = None """ - Sub-resource type (if applicable) + The arguments for Brave Search """ - arguments: dict[str, Any] | None = None + + +class BraveSearchArguments(BaseModel): """ - The arguments to pre-apply to the system call + Arguments for Brave Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str + """ + The search query for searching with Brave """ -class SystemDefUpdate(BaseModel): +class BraveSearchArgumentsUpdate(BaseModel): """ - System definition + Arguments for Brave Search """ model_config = ConfigDict( populate_by_name=True, ) - resource: ( - Literal["agent", "user", "task", "execution", "doc", "session", "job"] | None - ) = None + query: str | None = None """ - Resource is the name of the resource to use + The search query for searching with Brave """ - operation: ( - Literal[ - "create", - "update", - "patch", - "create_or_update", - "embed", - "change_status", - "search", - "chat", - "history", - "delete", - "get", - "list", - ] - | None - ) = None + + +class BraveSearchSetup(BaseModel): """ - Operation is the name of the operation to perform + Integration definition for Brave Search """ - resource_id: UUID | None = None + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str """ - Resource id (if applicable) + The api key for Brave Search """ - subresource: Literal["tool", "doc", "execution", "transition"] | None = None + + +class BraveSearchSetupUpdate(BaseModel): """ - Sub-resource type (if applicable) + Integration definition for Brave Search """ - arguments: dict[str, Any] | None = None + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str | None = None """ - The arguments to pre-apply to the system call + The api key for Brave Search """ -class Tool(BaseModel): +class BrowserbaseCompleteSessionArguments(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + id: str + status: Literal["REQUEST_RELEASE"] = "REQUEST_RELEASE" + + +class BrowserbaseCompleteSessionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str | None = None + status: Literal["REQUEST_RELEASE"] = "REQUEST_RELEASE" + + +class BrowserbaseContextArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str, Field(alias="projectId")] """ - Name of the tool (must be unique for this agent and a valid python identifier string ) + The Project ID. Can be found in Settings. """ - description: str | None = None + + +class BrowserbaseContextArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str | None, Field(alias="projectId")] = None """ - Description of the tool + The Project ID. Can be found in Settings. """ - function: FunctionDef | None = None + + +class BrowserbaseCreateSessionArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str | None, Field(alias="projectId")] = None """ - The function to call + The Project ID. Can be found in Settings. """ - integration: IntegrationDef | None = None + extension_id: Annotated[str | None, Field(alias="extensionId")] = None """ - The integration to call + The installed Extension ID. See Install Extension from GitHub. """ - system: SystemDef | None = None + browser_settings: Annotated[dict[str, Any], Field(alias="browserSettings")] = {} """ - The system to call + Browser settings """ - api_call: ApiCallDef | None = None + timeout: int = 3600 """ - The API call to make + Duration in seconds after which the session will automatically end. Defaults to the Project's defaultTimeout. """ - created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + keep_alive: Annotated[StrictBool, Field(alias="keepAlive")] = False """ - When this resource was created as UTC date-time + Set to true to keep the session alive even after disconnections. This is available on the Startup plan only. """ - updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + proxies: StrictBool | list[dict[str, Any]] = False """ - When this resource was updated as UTC date-time + Proxy configuration. Can be true for default proxy, or an array of proxy configurations. """ - id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] -class ToolResponse(BaseModel): +class BrowserbaseExtensionArguments(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - id: UUID - output: dict[str, Any] + repository_name: Annotated[str, Field(alias="repositoryName")] """ - The output of the tool + The GitHub repository name. + """ + ref: str | None = None + """ + Ref to install from a branch or tag. """ -class UpdateToolRequest(BaseModel): +class BrowserbaseExtensionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + repository_name: Annotated[str | None, Field(alias="repositoryName")] = None """ - Payload for updating a tool + The GitHub repository name. + """ + ref: str | None = None """ + Ref to install from a branch or tag. + """ + +class BrowserbaseGetSessionArguments(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + id: str + + +class BrowserbaseGetSessionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str | None = None + + +class BrowserbaseGetSessionConnectUrlArguments(BrowserbaseGetSessionArguments): + pass + + +class BrowserbaseGetSessionConnectUrlArgumentsUpdate( + BrowserbaseGetSessionArgumentsUpdate +): + pass + + +class BrowserbaseGetSessionLiveUrlsArguments(BrowserbaseGetSessionArguments): + pass + + +class BrowserbaseGetSessionLiveUrlsArgumentsUpdate( + BrowserbaseGetSessionArgumentsUpdate +): + pass + + +class BrowserbaseListSessionsArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal["RUNNING", "ERROR", "TIMED_OUT", "COMPLETED"] | None = None """ - Name of the tool (must be unique for this agent and a valid python identifier string ) + The status of the sessions to list (Available options: RUNNING, ERROR, TIMED_OUT, COMPLETED) """ - description: str | None = None + + +class BrowserbaseSetup(BaseModel): """ - Description of the tool + The setup parameters for the browserbase integration """ - function: FunctionDef | None = None + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str """ - The function to call + API key for the browserbase integration """ - integration: IntegrationDef | None = None + project_id: str """ - The integration to call + The project ID. Can be found in Settings. """ - system: SystemDef | None = None + api_url: str | None = None """ - The system to call + The API URL. Defaults to https://www.browserbase.com """ - api_call: ApiCallDef | None = None + connect_url: str | None = None """ - The API call to make + The connect URL. Defaults to wss://connect.browserbase.com + """ + + +class BrowserbaseSetupUpdate(BaseModel): + """ + The setup parameters for the browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str | None = None + """ + API key for the browserbase integration + """ + project_id: str | None = None + """ + The project ID. Can be found in Settings. + """ + api_url: str | None = None + """ + The API URL. Defaults to https://www.browserbase.com + """ + connect_url: str | None = None + """ + The connect URL. Defaults to wss://connect.browserbase.com + """ + + +class ChosenBash20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str | None = None + """ + The bash command to run + """ + restart: StrictBool = False + """ + Whether to restart the tool + """ + + +class ChosenComputer20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: Literal[ + "key", + "type", + "cursor_position", + "mouse_move", + "left_click", + "right_click", + "middle_click", + "double_click", + "screenshot", + ] + """ + The action to perform + """ + text: str | None = None + """ + The text to type + """ + coordinate: list[int] | None = None + """ + The (x, y) pixel coordinate to move the cursor to """ -class ChosenFunctionCall(ChosenToolCall): +class ChosenFunctionCall(BaseChosenToolCall): model_config = ConfigDict( populate_by_name=True, ) @@ -508,3 +690,1949 @@ class ChosenFunctionCall(ChosenToolCall): """ The function to call """ + + +class ChosenTextEditor20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: Literal["str_replace", "insert", "view", "undo_edit"] + """ + The command to run + """ + path: str + """ + The path to the file + """ + file_text: str | None = None + """ + The content of the file to be created + """ + insert_line: int | None = None + """ + The line to insert the new string after + """ + new_str: str | None = None + """ + The new string to insert + """ + old_str: str | None = None + """ + The string in the file to replace + """ + view_range: list[int] | None = None + """ + The line range to view + """ + + +class CloudinaryEditArguments(BaseModel): + """ + Arguments for Cloudinary media edit + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + public_id: str + """ + The file Public ID in Cloudinary + """ + transformation: list[dict[str, Any]] + """ + The transformation to apply to the file + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + + +class CloudinaryEditArgumentsUpdate(BaseModel): + """ + Arguments for Cloudinary media edit + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + public_id: str | None = None + """ + The file Public ID in Cloudinary + """ + transformation: list[dict[str, Any]] | None = None + """ + The transformation to apply to the file + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + + +class CloudinarySetup(BaseModel): + """ + Setup parameters for Cloudinary integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cloudinary_api_key: str + """ + The API key for Cloudinary + """ + cloudinary_api_secret: str + """ + The API secret for Cloudinary + """ + cloudinary_cloud_name: str + """ + The Cloud name for Cloudinary + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Cloudinary API + """ + + +class CloudinarySetupUpdate(BaseModel): + """ + Setup parameters for Cloudinary integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cloudinary_api_key: str | None = None + """ + The API key for Cloudinary + """ + cloudinary_api_secret: str | None = None + """ + The API secret for Cloudinary + """ + cloudinary_cloud_name: str | None = None + """ + The Cloud name for Cloudinary + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Cloudinary API + """ + + +class CloudinaryUploadArguments(BaseModel): + """ + Arguments for Cloudinary media upload + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + file: str + """ + The URL of the file upload + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + public_id: str | None = None + """ + Optional public ID for the uploaded file + """ + upload_params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class CloudinaryUploadArgumentsUpdate(BaseModel): + """ + Arguments for Cloudinary media upload + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + file: str | None = None + """ + The URL of the file upload + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + public_id: str | None = None + """ + Optional public ID for the uploaded file + """ + upload_params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class Computer20241022Def(BaseModel): + """ + Anthropic new tools + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["computer_20241022"] = "computer_20241022" + name: str = "computer" + display_width_px: Annotated[int, Field(ge=600)] = 1024 + """ + The display width in pixels + """ + display_height_px: Annotated[int, Field(ge=400)] = 768 + """ + The display height in pixels + """ + display_number: Annotated[int, Field(ge=1, le=10)] = 1 + """ + The display number to use + """ + + +class Computer20241022DefUpdate(Computer20241022Def): + """ + Anthropic new tools + """ + + +class CreateToolRequest(BaseModel): + """ + Payload for creating a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + + +class DummyIntegrationDef(BaseIntegrationDef): + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["dummy"] = "dummy" + + +class DummyIntegrationDefUpdate(BaseIntegrationDefUpdate): + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["dummy"] = "dummy" + + +class EmailArguments(BaseModel): + """ + Arguments for Email sending + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + to: str + """ + The email address to send the email to + """ + from_: Annotated[str, Field(alias="from")] + """ + The email address to send the email from + """ + subject: str + """ + The subject of the email + """ + body: str + """ + The body of the email + """ + + +class EmailArgumentsUpdate(BaseModel): + """ + Arguments for Email sending + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + to: str | None = None + """ + The email address to send the email to + """ + from_: Annotated[str | None, Field(alias="from")] = None + """ + The email address to send the email from + """ + subject: str | None = None + """ + The subject of the email + """ + body: str | None = None + """ + The body of the email + """ + + +class EmailIntegrationDef(BaseIntegrationDef): + """ + Email integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["email"] = "email" + """ + The provider must be "email" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: EmailSetup | None = None + """ + The setup parameters for Email + """ + arguments: EmailArguments | None = None + """ + The arguments for Email sending + """ + + +class EmailIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Email integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["email"] = "email" + """ + The provider must be "email" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: EmailSetupUpdate | None = None + """ + The setup parameters for Email + """ + arguments: EmailArgumentsUpdate | None = None + """ + The arguments for Email sending + """ + + +class EmailSetup(BaseModel): + """ + Setup parameters for Email integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + host: str + """ + The host of the email server + """ + port: int + """ + The port of the email server + """ + user: str + """ + The username of the email server + """ + password: str + """ + The password of the email server + """ + + +class EmailSetupUpdate(BaseModel): + """ + Setup parameters for Email integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + host: str | None = None + """ + The host of the email server + """ + port: int | None = None + """ + The port of the email server + """ + user: str | None = None + """ + The username of the email server + """ + password: str | None = None + """ + The password of the email server + """ + + +class FfmpegIntegrationDef(BaseIntegrationDef): + """ + Ffmpeg integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["ffmpeg"] = "ffmpeg" + """ + The provider must be "ffmpeg" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Ffmpeg + """ + arguments: FfmpegSearchArguments | None = None + """ + The arguments for Ffmpeg Search + """ + + +class FfmpegIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Ffmpeg integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["ffmpeg"] = "ffmpeg" + """ + The provider must be "ffmpeg" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Ffmpeg + """ + arguments: FfmpegSearchArgumentsUpdate | None = None + """ + The arguments for Ffmpeg Search + """ + + +class FfmpegSearchArguments(BaseModel): + """ + Arguments for Ffmpeg CMD + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cmd: str + """ + The bash command string + """ + file: str | None = None + """ + The base64 string of the file + """ + + +class FfmpegSearchArgumentsUpdate(BaseModel): + """ + Arguments for Ffmpeg CMD + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cmd: str | None = None + """ + The bash command string + """ + file: str | None = None + """ + The base64 string of the file + """ + + +class FunctionCallOption(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + """ + The name of the function + """ + arguments: str | None = None + """ + The parameters to pass to the function + """ + + +class FunctionDef(BaseModel): + """ + Function definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Any | None = None + """ + DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. + """ + description: Any | None = None + """ + DO NOT USE: This will be overriden by the tool description. Here only for compatibility reasons. + """ + parameters: dict[str, Any] | None = None + """ + The parameters the function accepts + """ + + +class LlamaParseFetchArguments(BaseModel): + """ + Arguments for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + filename: str | None = None + """ + File Name. If not provided, a random name will be generated. + """ + file: str | list[str] + """ + The base64 string of the file, which can be a single string or a list of strings + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + base64: StrictBool = False + """ + The input file is base64 + """ + + +class LlamaParseFetchArgumentsUpdate(BaseModel): + """ + Arguments for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + filename: str | None = None + """ + File Name. If not provided, a random name will be generated. + """ + file: str | list[str] | None = None + """ + The base64 string of the file, which can be a single string or a list of strings + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + base64: StrictBool = False + """ + The input file is base64 + """ + + +class LlamaParseIntegrationDef(BaseIntegrationDef): + """ + LlamaParse integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["llama_parse"] = "llama_parse" + """ + The provider must be "LlamaParseSetup" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: LlamaParseSetup | None = None + """ + The setup parameters for LlamaParse + """ + arguments: LlamaParseFetchArguments | None = None + """ + The arguments for LlamaParse + """ + + +class LlamaParseIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + LlamaParse integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["llama_parse"] = "llama_parse" + """ + The provider must be "LlamaParseSetup" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: LlamaParseSetupUpdate | None = None + """ + The setup parameters for LlamaParse + """ + arguments: LlamaParseFetchArgumentsUpdate | None = None + """ + The arguments for LlamaParse + """ + + +class LlamaParseSetup(BaseModel): + """ + Setup parameters for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + llamaparse_api_key: str + """ + The API key for LlamaParse + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class LlamaParseSetupUpdate(BaseModel): + """ + Setup parameters for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + llamaparse_api_key: str | None = None + """ + The API key for LlamaParse + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class NamedToolChoice(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + function: FunctionCallOption | None = None + + +class PatchToolRequest(BaseModel): + """ + Payload for patching a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str | None, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] = None + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: ( + Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + | None + ) = None + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDefUpdate + | BraveIntegrationDefUpdate + | EmailIntegrationDefUpdate + | SpiderIntegrationDefUpdate + | WikipediaIntegrationDefUpdate + | WeatherIntegrationDefUpdate + | BrowserbaseContextIntegrationDefUpdate + | BrowserbaseExtensionIntegrationDefUpdate + | BrowserbaseListSessionsIntegrationDefUpdate + | BrowserbaseCreateSessionIntegrationDefUpdate + | BrowserbaseGetSessionIntegrationDefUpdate + | BrowserbaseCompleteSessionIntegrationDefUpdate + | BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate + | BrowserbaseGetSessionConnectUrlIntegrationDefUpdate + | RemoteBrowserIntegrationDefUpdate + | LlamaParseIntegrationDefUpdate + | FfmpegIntegrationDefUpdate + | CloudinaryUploadIntegrationDefUpdate + | CloudinaryEditIntegrationDefUpdate + | ArxivIntegrationDefUpdate + | None + ) = None + """ + The integration to call + """ + system: SystemDefUpdate | None = None + """ + The system to call + """ + api_call: ApiCallDefUpdate | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022DefUpdate | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022DefUpdate | None = None + bash_20241022: Bash20241022DefUpdate | None = None + + +class RemoteBrowserArguments(BaseModel): + """ + The arguments for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + action: Literal[ + "key", + "type", + "mouse_move", + "left_click", + "left_click_drag", + "right_click", + "middle_click", + "double_click", + "screenshot", + "cursor_position", + "navigate", + "refresh", + ] + """ + The action to perform + """ + text: str | None = None + """ + The text + """ + coordinate: list | None = None + """ + The coordinate to move the mouse to + """ + + +class RemoteBrowserArgumentsUpdate(BaseModel): + """ + The arguments for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + action: ( + Literal[ + "key", + "type", + "mouse_move", + "left_click", + "left_click_drag", + "right_click", + "middle_click", + "double_click", + "screenshot", + "cursor_position", + "navigate", + "refresh", + ] + | None + ) = None + """ + The action to perform + """ + text: str | None = None + """ + The text + """ + coordinate: list | None = None + """ + The coordinate to move the mouse to + """ + + +class RemoteBrowserIntegrationDef(BaseIntegrationDef): + """ + The integration definition for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["remote_browser"] = "remote_browser" + setup: RemoteBrowserSetup + method: Literal["perform_action"] = "perform_action" + arguments: RemoteBrowserArguments | None = None + + +class RemoteBrowserIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + The integration definition for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["remote_browser"] = "remote_browser" + setup: RemoteBrowserSetup | None = None + method: Literal["perform_action"] = "perform_action" + arguments: RemoteBrowserArgumentsUpdate | None = None + + +class RemoteBrowserSetup(BaseModel): + """ + The setup parameters for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + width: int | None = None + """ + The width of the browser + """ + height: int | None = None + """ + The height of the browser + """ + + +class SpiderFetchArguments(BaseModel): + """ + Arguments for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: AnyUrl + """ + The URL to fetch data from + """ + mode: Literal["scrape"] = "scrape" + """ + The type of crawler to use + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Spider API + """ + + +class SpiderFetchArgumentsUpdate(BaseModel): + """ + Arguments for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: AnyUrl | None = None + """ + The URL to fetch data from + """ + mode: Literal["scrape"] = "scrape" + """ + The type of crawler to use + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Spider API + """ + + +class SpiderIntegrationDef(BaseIntegrationDef): + """ + Spider integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["spider"] = "spider" + """ + The provider must be "spider" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: SpiderSetup | None = None + """ + The setup parameters for Spider + """ + arguments: SpiderFetchArguments | None = None + """ + The arguments for Spider + """ + + +class SpiderIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Spider integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["spider"] = "spider" + """ + The provider must be "spider" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: SpiderSetupUpdate | None = None + """ + The setup parameters for Spider + """ + arguments: SpiderFetchArgumentsUpdate | None = None + """ + The arguments for Spider + """ + + +class SpiderSetup(BaseModel): + """ + Setup parameters for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + spider_api_key: str + """ + The API key for Spider + """ + + +class SpiderSetupUpdate(BaseModel): + """ + Setup parameters for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + spider_api_key: str | None = None + """ + The API key for Spider + """ + + +class SystemDef(BaseModel): + """ + System definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + resource: Literal["agent", "user", "task", "execution", "doc", "session", "job"] + """ + Resource is the name of the resource to use + """ + operation: Literal[ + "create", + "update", + "patch", + "create_or_update", + "embed", + "change_status", + "search", + "chat", + "history", + "delete", + "get", + "list", + ] + """ + Operation is the name of the operation to perform + """ + resource_id: UUID | None = None + """ + Resource id (if applicable) + """ + subresource: Literal["tool", "doc", "execution", "transition"] | None = None + """ + Sub-resource type (if applicable) + """ + arguments: dict[str, Any] | None = None + """ + The arguments to pre-apply to the system call + """ + + +class SystemDefUpdate(BaseModel): + """ + System definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + resource: ( + Literal["agent", "user", "task", "execution", "doc", "session", "job"] | None + ) = None + """ + Resource is the name of the resource to use + """ + operation: ( + Literal[ + "create", + "update", + "patch", + "create_or_update", + "embed", + "change_status", + "search", + "chat", + "history", + "delete", + "get", + "list", + ] + | None + ) = None + """ + Operation is the name of the operation to perform + """ + resource_id: UUID | None = None + """ + Resource id (if applicable) + """ + subresource: Literal["tool", "doc", "execution", "transition"] | None = None + """ + Sub-resource type (if applicable) + """ + arguments: dict[str, Any] | None = None + """ + The arguments to pre-apply to the system call + """ + + +class TextEditor20241022Def(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["text_editor_20241022"] = "text_editor_20241022" + name: str = "str_replace_editor" + + +class TextEditor20241022DefUpdate(TextEditor20241022Def): + pass + + +class Tool(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + + +class ToolResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + output: dict[str, Any] + """ + The output of the tool + """ + + +class UpdateToolRequest(BaseModel): + """ + Payload for updating a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + + +class WeatherGetArguments(BaseModel): + """ + Arguments for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + location: str + """ + The location for which to fetch weather data + """ + + +class WeatherGetArgumentsUpdate(BaseModel): + """ + Arguments for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + location: str | None = None + """ + The location for which to fetch weather data + """ + + +class WeatherIntegrationDef(BaseIntegrationDef): + """ + Weather integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["weather"] = "weather" + """ + The provider must be "weather" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: WeatherSetup | None = None + """ + The setup parameters for Weather + """ + arguments: WeatherGetArguments | None = None + """ + The arguments for Weather + """ + + +class WeatherIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Weather integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["weather"] = "weather" + """ + The provider must be "weather" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: WeatherSetupUpdate | None = None + """ + The setup parameters for Weather + """ + arguments: WeatherGetArgumentsUpdate | None = None + """ + The arguments for Weather + """ + + +class WeatherSetup(BaseModel): + """ + Integration definition for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + openweathermap_api_key: str + """ + The api key for OpenWeatherMap + """ + + +class WeatherSetupUpdate(BaseModel): + """ + Integration definition for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + openweathermap_api_key: str | None = None + """ + The api key for OpenWeatherMap + """ + + +class WikipediaIntegrationDef(BaseIntegrationDef): + """ + Wikipedia integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["wikipedia"] = "wikipedia" + """ + The provider must be "wikipedia" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Wikipedia + """ + arguments: WikipediaSearchArguments | None = None + """ + The arguments for Wikipedia Search + """ + + +class WikipediaIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Wikipedia integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["wikipedia"] = "wikipedia" + """ + The provider must be "wikipedia" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Wikipedia + """ + arguments: WikipediaSearchArgumentsUpdate | None = None + """ + The arguments for Wikipedia Search + """ + + +class WikipediaSearchArguments(BaseModel): + """ + Arguments for Wikipedia Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str + """ + The search query string + """ + load_max_docs: Annotated[int, Field(ge=1, le=10)] = 2 + """ + Maximum number of documents to load + """ + + +class WikipediaSearchArgumentsUpdate(BaseModel): + """ + Arguments for Wikipedia Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str | None = None + """ + The search query string + """ + load_max_docs: Annotated[int, Field(ge=1, le=10)] = 2 + """ + Maximum number of documents to load + """ + + +class ArxivIntegrationDef(BaseIntegrationDef): + """ + Arxiv integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["arxiv"] = "arxiv" + """ + The provider must be "arxiv" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Arxiv + """ + arguments: ArxivSearchArguments | None = None + """ + The arguments for Arxiv Search + """ + + +class ArxivIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Arxiv integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["arxiv"] = "arxiv" + """ + The provider must be "arxiv" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Arxiv + """ + arguments: ArxivSearchArgumentsUpdate | None = None + """ + The arguments for Arxiv Search + """ + + +class BaseBrowserbaseIntegrationDef(BaseIntegrationDef): + """ + The base definition for a browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["browserbase"] = "browserbase" + setup: BrowserbaseSetup | None = None + method: ( + Literal[ + "get_live_urls", + "list_sessions", + "create_session", + "get_session", + "complete_session", + "get_connect_url", + "install_extension_from_github", + "create_context", + "get_session_downloads", + "get_logs", + "get_recordings", + ] + | None + ) = None + arguments: Any | None = None + + +class BaseBrowserbaseIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + The base definition for a browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["browserbase"] = "browserbase" + setup: BrowserbaseSetupUpdate | None = None + method: ( + Literal[ + "get_live_urls", + "list_sessions", + "create_session", + "get_session", + "complete_session", + "get_connect_url", + "install_extension_from_github", + "create_context", + "get_session_downloads", + "get_logs", + "get_recordings", + ] + | None + ) = None + arguments: Any | None = None + + +class BaseCloudinaryIntegrationDef(BaseIntegrationDef): + """ + Base Cloudinary integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["cloudinary"] = "cloudinary" + setup: CloudinarySetup | None = None + method: Literal["media_upload", "media_edit"] | None = None + + +class BaseCloudinaryIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Base Cloudinary integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["cloudinary"] = "cloudinary" + setup: CloudinarySetupUpdate | None = None + method: Literal["media_upload", "media_edit"] | None = None + + +class BrowserbaseCompleteSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase complete session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["complete_session"] = "complete_session" + arguments: BrowserbaseCompleteSessionArguments | None = None + + +class BrowserbaseCompleteSessionIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase complete session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["complete_session"] = "complete_session" + arguments: BrowserbaseCompleteSessionArgumentsUpdate | None = None + + +class BrowserbaseContextIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase context provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_context"] = "create_context" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseContextArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseContextIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase context provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_context"] = "create_context" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseContextArgumentsUpdate | None = None + """ + The arguments for the method + """ + + +class BrowserbaseCreateSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase create session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_session"] = "create_session" + arguments: BrowserbaseCreateSessionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseCreateSessionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase create session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_session"] = "create_session" + arguments: BrowserbaseCreateSessionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseExtensionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase extension provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["install_extension_from_github"] | None = None + """ + The specific method of the integration to call + """ + arguments: BrowserbaseExtensionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseExtensionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase extension provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["install_extension_from_github"] | None = None + """ + The specific method of the integration to call + """ + arguments: BrowserbaseExtensionArgumentsUpdate | None = None + """ + The arguments for the method + """ + + +class BrowserbaseGetSessionConnectUrlIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session connect url integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_connect_url"] = "get_connect_url" + arguments: BrowserbaseGetSessionConnectUrlArguments | None = None + + +class BrowserbaseGetSessionConnectUrlIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase get session connect url integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_connect_url"] = "get_connect_url" + arguments: BrowserbaseGetSessionConnectUrlArgumentsUpdate | None = None + + +class BrowserbaseGetSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_session"] = "get_session" + arguments: BrowserbaseGetSessionArguments | None = None + + +class BrowserbaseGetSessionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase get session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_session"] = "get_session" + arguments: BrowserbaseGetSessionArgumentsUpdate | None = None + + +class BrowserbaseGetSessionLiveUrlsIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session live urls integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_live_urls"] = "get_live_urls" + arguments: BrowserbaseGetSessionLiveUrlsArguments | None = None + + +class BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase get session live urls integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_live_urls"] = "get_live_urls" + arguments: BrowserbaseGetSessionLiveUrlsArgumentsUpdate | None = None + + +class BrowserbaseListSessionsIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase list sessions integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["list_sessions"] = "list_sessions" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseListSessionsArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseListSessionsIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase list sessions integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["list_sessions"] = "list_sessions" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseListSessionsArguments | None = None + """ + The arguments for the method + """ + + +class CloudinaryEditIntegrationDef(BaseCloudinaryIntegrationDef): + """ + Cloudinary edit integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_edit"] = "media_edit" + arguments: CloudinaryEditArguments | None = None + + +class CloudinaryEditIntegrationDefUpdate(BaseCloudinaryIntegrationDefUpdate): + """ + Cloudinary edit integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_edit"] = "media_edit" + arguments: CloudinaryEditArgumentsUpdate | None = None + + +class CloudinaryUploadIntegrationDef(BaseCloudinaryIntegrationDef): + """ + Cloudinary upload integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_upload"] = "media_upload" + arguments: CloudinaryUploadArguments | None = None + + +class CloudinaryUploadIntegrationDefUpdate(BaseCloudinaryIntegrationDefUpdate): + """ + Cloudinary upload integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_upload"] = "media_upload" + arguments: CloudinaryUploadArgumentsUpdate | None = None diff --git a/agents-api/agents_api/autogen/Users.py b/agents-api/agents_api/autogen/Users.py index 6eeb1783d..720e21846 100644 --- a/agents-api/agents_api/autogen/Users.py +++ b/agents-api/agents_api/autogen/Users.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml from __future__ import annotations @@ -21,11 +21,10 @@ class CreateUserRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the user """ @@ -47,11 +46,10 @@ class PatchUserRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the user """ @@ -73,11 +71,10 @@ class UpdateUserRequest(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the user """ @@ -104,11 +101,10 @@ class User(BaseModel): name: Annotated[ str, Field( - "", max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), - ] + ] = "" """ Name of the user """ diff --git a/agents-api/agents_api/autogen/__init__.py b/agents-api/agents_api/autogen/__init__.py index 93bca4729..3d9935180 100644 --- a/agents-api/agents_api/autogen/__init__.py +++ b/agents-api/agents_api/autogen/__init__.py @@ -1,2 +1,2 @@ # generated by datamodel-codegen: -# filename: openapi-0.4.0.yaml +# filename: openapi-1.0.0.yaml diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index fd06f38a8..d19684cee 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -14,6 +14,7 @@ model_validator, ) +from ..common.storage_handler import RemoteObject from ..common.utils.datetime import utcnow from .Agents import * from .Chat import * @@ -21,6 +22,7 @@ from .Docs import * from .Entries import * from .Executions import * +from .Files import * from .Jobs import * from .Sessions import * from .Tasks import * @@ -76,6 +78,14 @@ class InputChatMLMessage(Message): pass +IntegrationDef = ( + BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef +) + # Patches # ------- @@ -312,13 +322,13 @@ def validate_subworkflows(self): ChatMLContent = ( list[ChatMLTextContentPart | ChatMLImageContentPart] | Tool - | ChosenToolCall + | BaseChosenToolCall | str | ToolResponse | list[ list[ChatMLTextContentPart | ChatMLImageContentPart] | Tool - | ChosenToolCall + | BaseChosenToolCall | str | ToolResponse ] @@ -347,6 +357,10 @@ def validate_subworkflows(self): # ------------- +class SystemDef(SystemDef): + arguments: dict[str, Any] | None | RemoteObject = None + + class CreateTransitionRequest(Transition): # The following fields are optional in this @@ -369,7 +383,7 @@ def from_model_input( model: str, *, role: ChatMLRole, - content: ChatMLContent, + content: ChatMLContent | None = None, name: str | None = None, source: ChatMLSource, **kwargs: dict, @@ -381,7 +395,7 @@ def from_model_input( return cls( role=role, - content=content, + content=content or [], name=name, source=source, tokenizer=tokenizer["type"], diff --git a/agents-api/agents_api/clients/async_s3.py b/agents-api/agents_api/clients/async_s3.py new file mode 100644 index 000000000..0cd5235ee --- /dev/null +++ b/agents-api/agents_api/clients/async_s3.py @@ -0,0 +1,121 @@ +from beartype import beartype +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + import botocore + from aiobotocore.session import get_session + from async_lru import alru_cache + from xxhash import xxh3_64_hexdigest as xxhash_key + + from ..env import ( + blob_store_bucket, + blob_store_cutoff_kb, + s3_access_key, + s3_endpoint, + s3_secret_key, + ) + + +async def list_buckets() -> list[str]: + session = get_session() + + async with session.create_client( + "s3", + endpoint_url=s3_endpoint, + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + ) as client: + data = await client.list_buckets() + buckets = [bucket["Name"] for bucket in data["Buckets"]] + return buckets + + +@alru_cache(maxsize=1) +async def setup(): + session = get_session() + + async with session.create_client( + "s3", + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + endpoint_url=s3_endpoint, + ) as client: + if blob_store_bucket not in await list_buckets(): + await client.create_bucket(Bucket=blob_store_bucket) + + +@alru_cache(maxsize=10_000) +async def exists(key: str) -> bool: + session = get_session() + + async with session.create_client( + "s3", + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + endpoint_url=s3_endpoint, + ) as client: + try: + await client.head_object(Bucket=blob_store_bucket, Key=key) + return True + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "404": + return False + else: + raise e + + +@beartype +async def add_object(key: str, body: bytes, replace: bool = False) -> None: + session = get_session() + + async with session.create_client( + "s3", + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + endpoint_url=s3_endpoint, + ) as client: + if replace: + await client.put_object(Bucket=blob_store_bucket, Key=key, Body=body) + return + + if await exists(key): + return + + await client.put_object(Bucket=blob_store_bucket, Key=key, Body=body) + + +@alru_cache(maxsize=256 * 1024 // max(1, blob_store_cutoff_kb)) # 256mb in cache +@beartype +async def get_object(key: str) -> bytes: + session = get_session() + + async with session.create_client( + "s3", + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + endpoint_url=s3_endpoint, + ) as client: + response = await client.get_object(Bucket=blob_store_bucket, Key=key) + body = await response["Body"].read() + return body + + +@beartype +async def delete_object(key: str) -> None: + session = get_session() + + async with session.create_client( + "s3", + aws_access_key_id=s3_access_key, + aws_secret_access_key=s3_secret_key, + endpoint_url=s3_endpoint, + ) as client: + await client.delete_object(Bucket=blob_store_bucket, Key=key) + + +@beartype +async def add_object_with_hash(body: bytes, replace: bool = False) -> str: + key = xxhash_key(body) + await add_object(key, body, replace=replace) + + return key diff --git a/agents-api/agents_api/clients/cozo.py b/agents-api/agents_api/clients/cozo.py index c184a46e2..285bae8b2 100644 --- a/agents-api/agents_api/clients/cozo.py +++ b/agents-api/agents_api/clients/cozo.py @@ -1,6 +1,7 @@ from typing import Dict from pycozo.client import Client +from pycozo_async import Client as AsyncClient from ..env import cozo_auth, cozo_host from ..web import app @@ -16,3 +17,13 @@ def get_cozo_client() -> Client: app.state.cozo_client = client return client + + +def get_async_cozo_client() -> AsyncClient: + client = getattr( + app.state, "async_cozo_client", AsyncClient("http", options=options) + ) + if not hasattr(app.state, "async_cozo_client"): + app.state.async_cozo_client = client + + return client diff --git a/agents-api/agents_api/clients/integrations.py b/agents-api/agents_api/clients/integrations.py index 5423fb664..cb66c293a 100644 --- a/agents-api/agents_api/clients/integrations.py +++ b/agents-api/agents_api/clients/integrations.py @@ -21,7 +21,7 @@ async def run_integration_service( setup = setup or None - async with AsyncClient() as client: + async with AsyncClient(timeout=600) as client: response = await client.post( url, json={"arguments": arguments, "setup": setup}, diff --git a/agents-api/agents_api/clients/litellm.py b/agents-api/agents_api/clients/litellm.py index 7cca97ae8..3b1860073 100644 --- a/agents-api/agents_api/clients/litellm.py +++ b/agents-api/agents_api/clients/litellm.py @@ -27,6 +27,25 @@ litellm.drop_params = True +def patch_litellm_response( + model_response: ModelResponse | CustomStreamWrapper, +) -> ModelResponse | CustomStreamWrapper: + """ + Patches the response we get from litellm to handle unexpected response formats. + """ + + if isinstance(model_response, ModelResponse): + for choice in model_response.choices: + if choice.finish_reason == "eos": + choice.finish_reason = "stop" + + elif isinstance(model_response, CustomStreamWrapper): + if model_response.received_finish_reason == "eos": + model_response.received_finish_reason = "stop" + + return model_response + + @wraps(_acompletion) @beartype async def acompletion( @@ -38,7 +57,14 @@ async def acompletion( supported_params = get_supported_openai_params(model) settings = {k: v for k, v in kwargs.items() if k in supported_params} - return await _acompletion( + # FIXME: This is a hotfix for Mistral API, which expects a different message format + if model[7:].startswith("mistral"): + messages = [ + {"role": message["role"], "content": message["content"]} + for message in messages + ] + + model_response = await _acompletion( model=model, messages=messages, **settings, @@ -46,6 +72,10 @@ async def acompletion( api_key=custom_api_key or litellm_master_key, ) + model_response = patch_litellm_response(model_response) + + return model_response + @wraps(_aembedding) @beartype @@ -58,8 +88,9 @@ async def aembedding( custom_api_key: None | str = None, **settings, ) -> list[list[float]]: - if not custom_api_key: - model = f"openai/{model}" # FIXME: This is for litellm + # Temporarily commented out (causes errors when using voyage/voyage-3) + # if not custom_api_key: + # model = f"openai/{model}" # FIXME: This is for litellm if isinstance(inputs, str): input = [inputs] diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 4bb25cbc9..6aaf0dcf3 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -1,11 +1,18 @@ from datetime import timedelta from uuid import UUID +from beartype import beartype from temporalio.client import Client, TLSConfig +from temporalio.common import ( + SearchAttributeKey, + SearchAttributePair, + TypedSearchAttributes, +) from ..autogen.openapi_model import TransitionTarget from ..common.protocol.tasks import ExecutionInput from ..common.retry_policies import DEFAULT_RETRY_POLICY +from ..common.storage_handler import store_in_blob_store_if_large from ..env import ( temporal_client_cert, temporal_namespace, @@ -37,17 +44,26 @@ async def get_client( ) +@beartype async def run_task_execution_workflow( *, execution_input: ExecutionInput, job_id: UUID, - start: TransitionTarget = TransitionTarget(workflow="main", step=0), - previous_inputs: list[dict] = [], + start: TransitionTarget | None = None, + previous_inputs: list[dict] | None = None, client: Client | None = None, ): from ..workflows.task_execution import TaskExecutionWorkflow + start: TransitionTarget = start or TransitionTarget(workflow="main", step=0) + previous_inputs: list[dict] = previous_inputs or [] + client = client or (await get_client()) + execution_id = execution_input.execution.id + execution_id_key = SearchAttributeKey.for_keyword("CustomStringField") + execution_input.arguments = await store_in_blob_store_if_large( + execution_input.arguments + ) return await client.start_workflow( TaskExecutionWorkflow.run, @@ -56,7 +72,11 @@ async def run_task_execution_workflow( id=str(job_id), run_timeout=timedelta(days=31), retry_policy=DEFAULT_RETRY_POLICY, - # TODO: Should add search_attributes for queryability + search_attributes=TypedSearchAttributes( + [ + SearchAttributePair(execution_id_key, str(execution_id)), + ] + ), ) diff --git a/agents-api/agents_api/common/exceptions/tasks.py b/agents-api/agents_api/common/exceptions/tasks.py new file mode 100644 index 000000000..425de8924 --- /dev/null +++ b/agents-api/agents_api/common/exceptions/tasks.py @@ -0,0 +1,187 @@ +""" +🎯 Error Handling: The Art of Knowing When to Try Again + +This module is like a bouncer at an error club - it decides which errors get a +second chance and which ones are permanently banned. Some errors are just having +a bad day (like network timeouts), while others are fundamentally problematic +(like trying to divide by zero... seriously, who does that?). + +Remember: To err is human, to retry divine... but only if it makes sense! +""" + +import asyncio +from typing import cast + +import beartype +import beartype.roar +import box +import box.exceptions +import fastapi +import httpx +import jinja2 +import jsonschema.exceptions +import litellm +import pydantic +import requests +import temporalio.exceptions +from tenacity import RetryError + +from .tools import IntegrationExecutionException + +# 🚫 The "No Second Chances" Club - errors that we won't retry +# Because sometimes, no means no! +NON_RETRYABLE_ERROR_TYPES = ( + # Temporal-specific errors (when time itself says no) + temporalio.exceptions.WorkflowAlreadyStartedError, + temporalio.exceptions.TerminatedError, + temporalio.exceptions.CancelledError, + # + # Built-in Python exceptions (the classics that never go out of style) + TypeError, + AssertionError, + SyntaxError, + ValueError, + ZeroDivisionError, # Because dividing by zero is still not cool + IndexError, + AttributeError, + LookupError, + BufferError, + ArithmeticError, + KeyError, + NameError, + NotImplementedError, + RecursionError, # When your code goes down the rabbit hole too deep + RuntimeError, + StopIteration, + StopAsyncIteration, + IndentationError, # Spaces vs tabs: the eternal debate + TabError, + # + # Unicode-related errors (when characters misbehave) + UnicodeError, + UnicodeEncodeError, + UnicodeDecodeError, + UnicodeTranslateError, + # + # HTTP and API-related errors (when the web says "nope") + fastapi.exceptions.RequestValidationError, + # + # Asynchronous programming errors (async/await gone wrong) + asyncio.CancelledError, + asyncio.InvalidStateError, + GeneratorExit, + # + # Third-party library exceptions (when other people's code says no) + jinja2.exceptions.TemplateSyntaxError, + jinja2.exceptions.TemplateNotFound, + jsonschema.exceptions.ValidationError, + pydantic.ValidationError, + requests.exceptions.InvalidURL, + requests.exceptions.MissingSchema, + # + # Box exceptions (when your box is broken) + box.exceptions.BoxKeyError, + box.exceptions.BoxTypeError, + box.exceptions.BoxValueError, + # + # Beartype exceptions (when your types are unbearable) + beartype.roar.BeartypeException, + beartype.roar.BeartypeDecorException, + beartype.roar.BeartypeDecorHintException, + beartype.roar.BeartypeDecorHintNonpepException, + beartype.roar.BeartypeDecorHintPepException, + beartype.roar.BeartypeDecorHintPepUnsupportedException, + beartype.roar.BeartypeDecorHintTypeException, + beartype.roar.BeartypeDecorParamException, + beartype.roar.BeartypeDecorParamNameException, + beartype.roar.BeartypeCallHintParamViolation, + beartype.roar.BeartypeCallHintReturnViolation, + beartype.roar.BeartypeDecorHintParamDefaultViolation, + beartype.roar.BeartypeDoorHintViolation, + # + # LiteLLM exceptions (when AI has a bad day) + litellm.exceptions.NotFoundError, + litellm.exceptions.InvalidRequestError, + litellm.exceptions.AuthenticationError, + litellm.exceptions.ServiceUnavailableError, + litellm.exceptions.OpenAIError, +) + +# 🔄 The "Try Again" Club - errors that deserve another shot +# Because everyone deserves a second chance... or third... or fourth... +RETRYABLE_ERROR_TYPES = ( + # LiteLLM exceptions (when AI needs a coffee break) + litellm.exceptions.RateLimitError, + litellm.exceptions.APIError, # Added to retry on "APIError: OpenAIException - Connection error" + # + # HTTP/Network related errors (internet having a bad hair day) + requests.exceptions.ConnectionError, + requests.exceptions.Timeout, + requests.exceptions.ConnectTimeout, + requests.exceptions.ReadTimeout, + httpx.ConnectError, + httpx.ConnectTimeout, + httpx.ReadTimeout, + httpx.WriteTimeout, + httpx.PoolTimeout, + # + # Standard library errors that are typically transient (like a bad mood) + ConnectionError, + TimeoutError, + OSError, # Covers many IO-related errors that may be transient + IOError, + # + # Database/storage related (when the database needs a nap) + asyncio.TimeoutError, + # + # Tenacity exceptions (retry when retrying goes wrong lol) + RetryError, + # + # Tools-related exceptions (when tools have a bad day) + IntegrationExecutionException, +) + +# HTTP status codes that say "maybe try again later?" +RETRYABLE_HTTP_STATUS_CODES = ( + 408, # Request Timeout (server needs a coffee break) + 429, # Too Many Requests (slow down, speedster!) + 503, # Service Unavailable (server is having a moment) + 504, # Gateway Timeout (the internet took a detour) +) + + +def is_retryable_error(error: BaseException) -> bool: + """ + The Great Error Judge: Decides if an error deserves another chance at life. + + Think of this function as a very understanding but firm teacher - some mistakes + get a do-over, others are learning opportunities (aka failures). + + Args: + error (Exception): The error that's pleading its case + + Returns: + bool: True if the error gets another shot, False if it's game over + """ + # First, check if it's in the "permanently banned" list + if isinstance(error, NON_RETRYABLE_ERROR_TYPES): + return False + + # Check if it's in the "VIP retry club" + if isinstance(error, RETRYABLE_ERROR_TYPES): + return True + + # Special handling for HTTP errors (because they're special snowflakes) + if isinstance(error, fastapi.exceptions.HTTPException): + error = cast(fastapi.exceptions.HTTPException, error) + if error.status_code in RETRYABLE_HTTP_STATUS_CODES: + return True + + if isinstance(error, httpx.HTTPStatusError): + error = cast(httpx.HTTPStatusError, error) + if error.response.status_code in RETRYABLE_HTTP_STATUS_CODES: + return True + + # If we don't know this error, we play it safe and don't retry + # (stranger danger!) + return False diff --git a/agents-api/agents_api/common/exceptions/tools.py b/agents-api/agents_api/common/exceptions/tools.py new file mode 100644 index 000000000..2ea126505 --- /dev/null +++ b/agents-api/agents_api/common/exceptions/tools.py @@ -0,0 +1,25 @@ +""" +Defines tools-related exceptions for the agents API. +""" + +from ...autogen.openapi_model import BaseIntegrationDef +from . import BaseCommonException + + +class BaseToolsException(BaseCommonException): + """Base exception for tools-related errors.""" + + pass + + +class IntegrationExecutionException(BaseToolsException): + """Exception raised when an error occurs during an integration execution.""" + + def __init__(self, integration: BaseIntegrationDef, error: str): + integration_str = integration.provider + ( + "." + integration.method if integration.method else "" + ) + super().__init__( + f"Error in executing {integration_str}: {error}", + http_code=500, + ) diff --git a/agents-api/agents_api/common/interceptors.py b/agents-api/agents_api/common/interceptors.py new file mode 100644 index 000000000..40600a818 --- /dev/null +++ b/agents-api/agents_api/common/interceptors.py @@ -0,0 +1,139 @@ +""" +This module defines custom interceptors for Temporal activities and workflows. +The main purpose of these interceptors is to handle errors and prevent retrying +certain types of errors that are known to be non-retryable. +""" + +from typing import Optional, Type + +from temporalio.activity import _CompleteAsyncError as CompleteAsyncError +from temporalio.exceptions import ApplicationError, FailureError, TemporalError +from temporalio.service import RPCError +from temporalio.worker import ( + ActivityInboundInterceptor, + ExecuteActivityInput, + ExecuteWorkflowInput, + Interceptor, + WorkflowInboundInterceptor, + WorkflowInterceptorClassInput, +) +from temporalio.workflow import ( + ContinueAsNewError, + NondeterminismError, + ReadOnlyContextError, +) + +from .exceptions.tasks import is_retryable_error + + +class CustomActivityInterceptor(ActivityInboundInterceptor): + """ + Custom interceptor for Temporal activities. + + This interceptor catches exceptions during activity execution and + raises them as non-retryable ApplicationErrors if they are identified + as non-retryable errors. + """ + + async def execute_activity(self, input: ExecuteActivityInput): + """ + 🎭 The Activity Whisperer: Handles activity execution with style and grace + + This is like a safety net for your activities - catching errors and deciding + their fate with the wisdom of a fortune cookie. + """ + try: + return await super().execute_activity(input) + except ( + ContinueAsNewError, # When you need a fresh start + ReadOnlyContextError, # When someone tries to write in a museum + NondeterminismError, # When chaos theory kicks in + RPCError, # When computers can't talk to each other + CompleteAsyncError, # When async goes wrong + TemporalError, # When time itself rebels + FailureError, # When failure is not an option, but happens anyway + ApplicationError, # When the app says "nope" + ): + raise + except BaseException as e: + if not is_retryable_error(e): + # If it's not retryable, we wrap it in a nice bow (ApplicationError) + # and mark it as non-retryable to prevent further attempts + raise ApplicationError( + str(e), + type=type(e).__name__, + non_retryable=True, + ) + # For retryable errors, we'll let Temporal retry with backoff + # Default retry policy ensures at least 2 retries + raise + + +class CustomWorkflowInterceptor(WorkflowInboundInterceptor): + """ + 🎪 The Workflow Circus Ringmaster + + This interceptor is like a circus ringmaster - keeping all the workflow acts + running smoothly and catching any lions (errors) that escape their cages. + """ + + async def execute_workflow(self, input: ExecuteWorkflowInput): + """ + 🎪 The Main Event: Workflow Execution Extravaganza! + + Watch as we gracefully handle errors like a trapeze artist catching their partner! + """ + try: + return await super().execute_workflow(input) + except ( + ContinueAsNewError, # The show must go on! + ReadOnlyContextError, # No touching, please! + NondeterminismError, # When butterflies cause hurricanes + RPCError, # Lost in translation + CompleteAsyncError, # Async said "bye" too soon + TemporalError, # Time is relative, errors are absolute + FailureError, # Task failed successfully + ApplicationError, # App.exe has stopped working + ): + raise + except BaseException as e: + if not is_retryable_error(e): + # Pack the error in a nice box with a "do not retry" sticker + raise ApplicationError( + str(e), + type=type(e).__name__, + non_retryable=True, + ) + # Let it retry - everyone deserves a second (or third) chance! + raise + + +class CustomInterceptor(Interceptor): + """ + 🎭 The Grand Interceptor: Master of Ceremonies + + This is like the backstage manager of a theater - making sure both the + activity actors and workflow directors have their interceptor costumes on. + """ + + def intercept_activity( + self, next: ActivityInboundInterceptor + ) -> ActivityInboundInterceptor: + """ + 🎬 Activity Interceptor Factory: Where the magic begins! + + Creating custom activity interceptors faster than a caffeinated barista + makes espresso shots. + """ + return CustomActivityInterceptor(super().intercept_activity(next)) + + def workflow_interceptor_class( + self, input: WorkflowInterceptorClassInput + ) -> Optional[Type[WorkflowInboundInterceptor]]: + """ + 🎪 Workflow Interceptor Class Selector + + Like a matchmaker for workflows and their interceptors - a match made in + exception handling heaven! + """ + return CustomWorkflowInterceptor diff --git a/agents-api/agents_api/common/nlp.py b/agents-api/agents_api/common/nlp.py new file mode 100644 index 000000000..d7dcabe15 --- /dev/null +++ b/agents-api/agents_api/common/nlp.py @@ -0,0 +1,286 @@ +import re +from collections import Counter, defaultdict +from functools import lru_cache + +import spacy +from spacy.matcher import PhraseMatcher +from spacy.tokens import Doc +from spacy.util import filter_spans + +# Precompile regex patterns +WHITESPACE_RE = re.compile(r"\s+") +NON_ALPHANUM_RE = re.compile(r"[^\w\s\-_]+") + +# Initialize spaCy with minimal pipeline +nlp = spacy.load("en_core_web_sm", exclude=["lemmatizer", "textcat", "tok2vec"]) + +# Add sentencizer for faster sentence tokenization +sentencizer = nlp.add_pipe("sentencizer") + + +# Singleton PhraseMatcher for better performance +class KeywordMatcher: + _instance = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance.matcher = PhraseMatcher(nlp.vocab, attr="LOWER") + cls._instance.batch_size = 1000 # Adjust based on memory constraints + cls._instance.patterns_cache = {} + return cls._instance + + @lru_cache(maxsize=10000) + def _create_pattern(self, text: str) -> Doc: + return nlp.make_doc(text) + + def find_matches(self, doc: Doc, keywords: list[str]) -> dict[str, list[int]]: + """Batch process keywords for better performance.""" + keyword_positions = defaultdict(list) + + # Process keywords in batches to avoid memory issues + for i in range(0, len(keywords), self.batch_size): + batch = keywords[i : i + self.batch_size] + patterns = [self._create_pattern(kw) for kw in batch] + + # Clear previous patterns and add new batch + if "KEYWORDS" in self.matcher: + self.matcher.remove("KEYWORDS") + self.matcher.add("KEYWORDS", patterns) + + # Find matches for this batch + matches = self.matcher(doc) + for match_id, start, end in matches: + span_text = doc[start:end].text + normalized = WHITESPACE_RE.sub(" ", span_text).lower().strip() + keyword_positions[normalized].append(start) + + return keyword_positions + + +# Initialize global matcher +keyword_matcher = KeywordMatcher() + + +@lru_cache(maxsize=10000) +def clean_keyword(kw: str) -> str: + """Cache cleaned keywords for reuse.""" + return NON_ALPHANUM_RE.sub("", kw).strip() + + +def extract_keywords(doc: Doc, top_n: int = 10, clean: bool = True) -> list[str]: + """Optimized keyword extraction with minimal behavior change.""" + excluded_labels = { + "DATE", + "TIME", + "PERCENT", + "MONEY", + "QUANTITY", + "ORDINAL", + "CARDINAL", + } + + # Extract and filter spans in a single pass + ent_spans = [ent for ent in doc.ents if ent.label_ not in excluded_labels] + chunk_spans = [chunk for chunk in doc.noun_chunks if not chunk.root.is_stop] + all_spans = filter_spans(ent_spans + chunk_spans) + + # Process spans efficiently + keywords = [] + seen_texts = set() + + for span in all_spans: + text = span.text.strip() + lower_text = text.lower() + + # Skip empty or seen texts + if not text or lower_text in seen_texts: + continue + + seen_texts.add(lower_text) + keywords.append(text) + + # Normalize keywords by replacing multiple spaces with single space and stripping + normalized_keywords = [WHITESPACE_RE.sub(" ", kw).strip() for kw in keywords] + + # Count frequencies efficiently + freq = Counter(normalized_keywords) + top_keywords = [kw for kw, _ in freq.most_common(top_n)] + + if clean: + return [clean_keyword(kw) for kw in top_keywords] + return top_keywords + + +def find_proximity_groups( + keywords: list[str], keyword_positions: dict[str, list[int]], n: int = 10 +) -> list[set[str]]: + """Optimized proximity grouping using sorted positions.""" + # Early return for single or no keywords + if len(keywords) <= 1: + return [{kw} for kw in keywords] + + # Create flat list of positions for efficient processing + positions: list[tuple[int, str]] = [ + (pos, kw) for kw in keywords for pos in keyword_positions[kw] + ] + + # Sort positions once + positions.sort() + + # Initialize Union-Find with path compression and union by rank + parent = {kw: kw for kw in keywords} + rank = {kw: 0 for kw in keywords} + + def find(u: str) -> str: + if parent[u] != u: + parent[u] = find(parent[u]) + return parent[u] + + def union(u: str, v: str) -> None: + u_root, v_root = find(u), find(v) + if u_root != v_root: + if rank[u_root] < rank[v_root]: + u_root, v_root = v_root, u_root + parent[v_root] = u_root + if rank[u_root] == rank[v_root]: + rank[u_root] += 1 + + # Use sliding window for proximity checking + window = [] + for pos, kw in positions: + # Remove positions outside window + while window and pos - window[0][0] > n: + window.pop(0) + + # Union with all keywords in window + for _, w_kw in window: + union(kw, w_kw) + + window.append((pos, kw)) + + # Group keywords efficiently + groups = defaultdict(set) + for kw in keywords: + root = find(kw) + groups[root].add(kw) + + return list(groups.values()) + + +def build_query_pattern(group_size: int, n: int) -> str: + """Cache query patterns for common group sizes.""" + if group_size == 1: + return '"{}"' + return f"NEAR/{n}(" + " ".join('"{}"' for _ in range(group_size)) + ")" + + +def build_query(groups: list[set[str]], n: int = 10) -> str: + """Build query with cached patterns.""" + clauses = [] + + for group in groups: + if len(group) == 1: + clauses.append(f'"{next(iter(group))}"') + else: + # Sort by length descending to prioritize longer phrases + sorted_group = sorted(group, key=len, reverse=True) + # Get cached pattern and format with keywords + pattern = build_query_pattern(len(group), n) + clause = pattern.format(*sorted_group) + clauses.append(clause) + + return " OR ".join(clauses) + + +@lru_cache(maxsize=100) +def paragraph_to_custom_queries( + paragraph: str, top_n: int = 10, proximity_n: int = 10, min_keywords: int = 1 +) -> list[str]: + """ + Optimized paragraph processing with minimal behavior changes. + Added min_keywords parameter to filter out low-value queries. + + Args: + paragraph (str): The input paragraph to convert. + top_n (int): Number of top keywords to extract per sentence. + proximity_n (int): The proximity window for NEAR/n. + min_keywords (int): Minimum number of keywords required to form a query. + + Returns: + list[str]: The list of custom query strings. + """ + if not paragraph or not paragraph.strip(): + return [] + + # Process entire paragraph once + doc = nlp(paragraph) + queries = [] + + # Process sentences + for sent in doc.sents: + # Convert to doc for consistent API + sent_doc = sent.as_doc() + + # Extract and clean keywords + keywords = extract_keywords(sent_doc, top_n) + if len(keywords) < min_keywords: + continue + + # Find keyword positions using matcher + keyword_positions = keyword_matcher.find_matches(sent_doc, keywords) + + # Skip if no keywords found in positions + if not keyword_positions: + continue + + # Find proximity groups and build query + groups = find_proximity_groups(keywords, keyword_positions, proximity_n) + query = build_query(groups, proximity_n) + + if query: + queries.append(query) + + return queries + + +def batch_paragraphs_to_custom_queries( + paragraphs: list[str], + top_n: int = 10, + proximity_n: int = 10, + min_keywords: int = 1, + n_process: int = 1, +) -> list[list[str]]: + """ + Processes multiple paragraphs using nlp.pipe for better performance. + + Args: + paragraphs (list[str]): list of paragraphs to process. + top_n (int): Number of top keywords to extract per sentence. + proximity_n (int): The proximity window for NEAR/n. + min_keywords (int): Minimum number of keywords required to form a query. + n_process (int): Number of processes to use for multiprocessing. + + Returns: + list[list[str]]: A list where each element is a list of queries for a paragraph. + """ + results = [] + for doc in nlp.pipe( + paragraphs, disable=["lemmatizer", "textcat"], n_process=n_process + ): + queries = [] + for sent in doc.sents: + sent_doc = sent.as_doc() + keywords = extract_keywords(sent_doc, top_n) + if len(keywords) < min_keywords: + continue + keyword_positions = keyword_matcher.find_matches(sent_doc, keywords) + if not keyword_positions: + continue + groups = find_proximity_groups(keywords, keyword_positions, proximity_n) + query = build_query(groups, proximity_n) + if query: + queries.append(query) + results.append(queries) + + return results diff --git a/agents-api/agents_api/common/protocol/remote.py b/agents-api/agents_api/common/protocol/remote.py new file mode 100644 index 000000000..ce2a2a63a --- /dev/null +++ b/agents-api/agents_api/common/protocol/remote.py @@ -0,0 +1,91 @@ +from dataclasses import dataclass +from typing import Any + +from temporalio import activity, workflow + +with workflow.unsafe.imports_passed_through(): + from pydantic import BaseModel + + from ...env import blob_store_bucket + + +@dataclass +class RemoteObject: + key: str + bucket: str = blob_store_bucket + + +class BaseRemoteModel(BaseModel): + _remote_cache: dict[str, Any] + + class Config: + arbitrary_types_allowed = True + + def __init__(self, **data: Any): + super().__init__(**data) + self._remote_cache = {} + + async def load_item(self, item: Any | RemoteObject) -> Any: + if not activity.in_activity(): + return item + + from ..storage_handler import load_from_blob_store_if_remote + + return await load_from_blob_store_if_remote(item) + + async def save_item(self, item: Any) -> Any: + if not activity.in_activity(): + return item + + from ..storage_handler import store_in_blob_store_if_large + + return await store_in_blob_store_if_large(item) + + async def get_attribute(self, name: str) -> Any: + if name.startswith("_"): + return super().__getattribute__(name) + + try: + value = super().__getattribute__(name) + except AttributeError: + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{name}'" + ) + + if isinstance(value, RemoteObject): + cache = super().__getattribute__("_remote_cache") + if name in cache: + return cache[name] + + loaded_data = await self.load_item(value) + cache[name] = loaded_data + return loaded_data + + return value + + async def set_attribute(self, name: str, value: Any) -> None: + if name.startswith("_"): + super().__setattr__(name, value) + return + + stored_value = await self.save_item(value) + super().__setattr__(name, stored_value) + + if isinstance(stored_value, RemoteObject): + cache = self.__dict__.get("_remote_cache", {}) + cache.pop(name, None) + + async def load_all(self) -> None: + for name in self.model_fields_set: + await self.get_attribute(name) + + async def unload_attribute(self, name: str) -> None: + if name in self._remote_cache: + data = self._remote_cache.pop(name) + remote_obj = await self.save_item(data) + super().__setattr__(name, remote_obj) + + async def unload_all(self) -> "BaseRemoteModel": + for name in list(self._remote_cache.keys()): + await self.unload_attribute(name) + return self diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index bbb5c28d3..430a62f36 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -1,30 +1,40 @@ -from typing import Annotated, Any, Type +import asyncio +from typing import Annotated, Any, Literal from uuid import UUID -from pydantic import BaseModel, Field, computed_field -from pydantic_partial import create_partial_model - -from ...autogen.openapi_model import ( - Agent, - CreateTaskRequest, - CreateTransitionRequest, - Execution, - ExecutionStatus, - PartialTaskSpecDef, - PatchTaskRequest, - Session, - Task, - TaskSpec, - TaskSpecDef, - TaskToolDef, - Tool, - TransitionTarget, - TransitionType, - UpdateTaskRequest, - User, - Workflow, - WorkflowStep, -) +from beartype import beartype +from temporalio import activity, workflow +from temporalio.exceptions import ApplicationError + +with workflow.unsafe.imports_passed_through(): + from pydantic import BaseModel, Field, computed_field + from pydantic_partial import create_partial_model + + from ...autogen.openapi_model import ( + Agent, + CreateTaskRequest, + CreateToolRequest, + CreateTransitionRequest, + Execution, + ExecutionStatus, + PartialTaskSpecDef, + PatchTaskRequest, + Session, + Task, + TaskSpec, + TaskSpecDef, + TaskToolDef, + Tool, + ToolRef, + TransitionTarget, + TransitionType, + UpdateTaskRequest, + User, + Workflow, + WorkflowStep, + ) + from ...common.storage_handler import load_from_blob_store_if_remote + from .remote import BaseRemoteModel, RemoteObject # TODO: Maybe we should use a library for this @@ -66,7 +76,15 @@ valid_transitions: dict[TransitionType, list[TransitionType]] = { # Start state "init": ["wait", "error", "step", "cancelled", "init_branch", "finish"], - "init_branch": ["wait", "error", "step", "cancelled", "finish_branch"], + "init_branch": [ + "wait", + "error", + "step", + "cancelled", + "init_branch", + "finish_branch", + "finish", + ], # End states "finish": [], "error": [], @@ -118,7 +136,8 @@ } # type: ignore -PartialTransition: Type[BaseModel] = create_partial_model(CreateTransitionRequest) +class PartialTransition(create_partial_model(CreateTransitionRequest)): + user_state: dict[str, Any] = Field(default_factory=dict) class ExecutionInput(BaseModel): @@ -126,35 +145,59 @@ class ExecutionInput(BaseModel): execution: Execution task: TaskSpecDef agent: Agent - agent_tools: list[Tool] - arguments: dict[str, Any] + agent_tools: list[Tool | CreateToolRequest] + arguments: dict[str, Any] | RemoteObject # Not used at the moment user: User | None = None session: Session | None = None -class StepContext(BaseModel): - execution_input: ExecutionInput - inputs: list[Any] +class StepContext(BaseRemoteModel): + execution_input: ExecutionInput | RemoteObject + inputs: list[Any] | RemoteObject cursor: TransitionTarget @computed_field @property - def tools(self) -> list[Tool]: + def tools(self) -> list[Tool | CreateToolRequest]: execution_input = self.execution_input task = execution_input.task agent_tools = execution_input.agent_tools + step_tools: Literal["all"] | list[ToolRef | CreateToolRequest] = getattr( + self.current_step, "tools", "all" + ) + + if step_tools != "all": + if not all( + tool and isinstance(tool, CreateToolRequest) for tool in step_tools + ): + raise ApplicationError( + "Invalid tools for step (ToolRef not supported yet)" + ) + + return step_tools + + # Need to convert task.tools (list[TaskToolDef]) to list[Tool] + task_tools = [] + for tool in task.tools: + tool_def = tool.model_dump() + task_tools.append( + CreateToolRequest( + **{tool_def["type"]: tool_def.pop("spec"), **tool_def} + ) + ) + if not task.inherit_tools: - return task.tools + return task_tools # Remove duplicates from agent_tools filtered_tools = [ t for t in agent_tools if t.name not in map(lambda x: x.name, task.tools) ] - return filtered_tools + task.tools + return filtered_tools + task_tools @computed_field @property @@ -195,17 +238,33 @@ def is_main(self) -> Annotated[bool, Field(exclude=True)]: def model_dump(self, *args, **kwargs) -> dict[str, Any]: dump = super().model_dump(*args, **kwargs) + execution_input: dict = dump.pop("execution_input") + + return dump | execution_input + + async def prepare_for_step( + self, *args, include_remote: bool = True, **kwargs + ) -> dict[str, Any]: + current_input = self.current_input + inputs = self.inputs + if activity.in_activity() and include_remote: + await self.load_all() + inputs = await asyncio.gather( + *[load_from_blob_store_if_remote(input) for input in inputs] + ) + current_input = await load_from_blob_store_if_remote(current_input) # Merge execution inputs into the dump dict - execution_input: dict = dump.pop("execution_input") - current_input: Any = dump.pop("current_input") - dump = { - **dump, - **execution_input, - "_": current_input, - } + dump = self.model_dump(*args, **kwargs) + dump["inputs"] = inputs + prepared = dump | {"_": current_input} - return dump + for i, input in enumerate(inputs): + prepared = prepared | {f"_{i}": input} + if i >= 100: + break + + return prepared class StepOutcome(BaseModel): @@ -214,10 +273,11 @@ class StepOutcome(BaseModel): transition_to: tuple[TransitionType, TransitionTarget] | None = None +@beartype def task_to_spec( task: Task | CreateTaskRequest | UpdateTaskRequest | PatchTaskRequest, **model_opts ) -> TaskSpecDef | PartialTaskSpecDef: - task_data = task.model_dump(**model_opts) + task_data = task.model_dump(**model_opts, exclude={"task_id", "id", "agent_id"}) if "tools" in task_data: del task_data["tools"] @@ -226,13 +286,12 @@ def task_to_spec( for tool in task.tools: tool_spec = getattr(tool, tool.type) - tools.append( - TaskToolDef( - type=tool.type, - spec=tool_spec.model_dump(), - **tool.model_dump(exclude={"type"}), - ) + tool_obj = dict( + type=tool.type, + spec=tool_spec.model_dump(), + **tool.model_dump(exclude={"type"}), ) + tools.append(TaskToolDef(**tool_obj)) workflows = [Workflow(name="main", steps=task_data.pop("main"))] diff --git a/agents-api/agents_api/common/retry_policies.py b/agents-api/agents_api/common/retry_policies.py index fc343553c..c6c1c362c 100644 --- a/agents-api/agents_api/common/retry_policies.py +++ b/agents-api/agents_api/common/retry_policies.py @@ -1,63 +1,14 @@ -from datetime import timedelta +# from datetime import timedelta -from temporalio.common import RetryPolicy +# from temporalio.common import RetryPolicy -DEFAULT_RETRY_POLICY = RetryPolicy( - initial_interval=timedelta(seconds=1), - backoff_coefficient=2, - maximum_attempts=25, - maximum_interval=timedelta(seconds=300), - non_retryable_error_types=[ - # Temporal-specific errors - "WorkflowExecutionAlreadyStarted", - "temporalio.exceptions.TerminalFailure", - "temporalio.exceptions.CanceledError", - # - # Built-in Python exceptions - "TypeError", - "AssertionError", - "SyntaxError", - "ValueError", - "ZeroDivisionError", - "IndexError", - "AttributeError", - "LookupError", - "BufferError", - "ArithmeticError", - "KeyError", - "NameError", - "NotImplementedError", - "RecursionError", - "RuntimeError", - "StopIteration", - "StopAsyncIteration", - "IndentationError", - "TabError", - # - # Unicode-related errors - "UnicodeError", - "UnicodeEncodeError", - "UnicodeDecodeError", - "UnicodeTranslateError", - # - # HTTP and API-related errors - "HTTPException", - "fastapi.exceptions.HTTPException", - "fastapi.exceptions.RequestValidationError", - "httpx.RequestError", - "httpx.HTTPStatusError", - # - # Asynchronous programming errors - "asyncio.CancelledError", - "asyncio.InvalidStateError", - "GeneratorExit", - # - # Third-party library exceptions - "jinja2.exceptions.TemplateSyntaxError", - "jinja2.exceptions.TemplateNotFound", - "jsonschema.exceptions.ValidationError", - "pydantic.ValidationError", - "requests.exceptions.InvalidURL", - "requests.exceptions.MissingSchema", - ], -) +# DEFAULT_RETRY_POLICY = RetryPolicy( +# initial_interval=timedelta(seconds=1), +# backoff_coefficient=2, +# maximum_attempts=25, +# maximum_interval=timedelta(seconds=300), +# ) + +# FIXME: Adding both interceptors and retry policy (even with `non_retryable_errors` not set) +# is causing the errors to be retried. We need to find a workaround for this. +DEFAULT_RETRY_POLICY = None diff --git a/agents-api/agents_api/common/storage_handler.py b/agents-api/agents_api/common/storage_handler.py new file mode 100644 index 000000000..53f6f255b --- /dev/null +++ b/agents-api/agents_api/common/storage_handler.py @@ -0,0 +1,224 @@ +import asyncio +import sys +from datetime import timedelta +from functools import wraps +from typing import Any, Callable + +from pydantic import BaseModel +from temporalio import workflow + +from ..activities.sync_items_remote import load_inputs_remote +from ..clients import async_s3 +from ..common.protocol.remote import BaseRemoteModel, RemoteObject +from ..common.retry_policies import DEFAULT_RETRY_POLICY +from ..env import ( + blob_store_cutoff_kb, + debug, + temporal_schedule_to_close_timeout, + testing, + use_blob_store_for_temporal, +) +from ..worker.codec import deserialize, serialize + + +async def store_in_blob_store_if_large(x: Any) -> RemoteObject | Any: + if not use_blob_store_for_temporal: + return x + + await async_s3.setup() + + serialized = serialize(x) + data_size = sys.getsizeof(serialized) + + if data_size > blob_store_cutoff_kb * 1024: + key = await async_s3.add_object_with_hash(serialized) + return RemoteObject(key=key) + + return x + + +async def load_from_blob_store_if_remote(x: Any | RemoteObject) -> Any: + if not use_blob_store_for_temporal: + return x + + await async_s3.setup() + + if isinstance(x, RemoteObject): + fetched = await async_s3.get_object(x.key) + return deserialize(fetched) + + elif isinstance(x, dict) and set(x.keys()) == {"bucket", "key"}: + fetched = await async_s3.get_object(x["key"]) + return deserialize(fetched) + + return x + + +# Decorator that automatically does two things: +# 1. store in blob store if the output of a function is large +# 2. load from blob store if the input is a RemoteObject + + +def auto_blob_store(f: Callable | None = None, *, deep: bool = False) -> Callable: + def auto_blob_store_decorator(f: Callable) -> Callable: + async def load_args( + args: list | tuple, kwargs: dict[str, Any] + ) -> tuple[list | tuple, dict[str, Any]]: + new_args = await asyncio.gather( + *[load_from_blob_store_if_remote(arg) for arg in args] + ) + kwargs_keys, kwargs_values = list(zip(*kwargs.items())) or ([], []) + new_kwargs = await asyncio.gather( + *[load_from_blob_store_if_remote(v) for v in kwargs_values] + ) + new_kwargs = dict(zip(kwargs_keys, new_kwargs)) + + if deep: + args = new_args + kwargs = new_kwargs + + new_args = [] + + for arg in args: + if isinstance(arg, list): + new_args.append( + await asyncio.gather( + *[load_from_blob_store_if_remote(item) for item in arg] + ) + ) + elif isinstance(arg, dict): + keys, values = list(zip(*arg.items())) or ([], []) + values = await asyncio.gather( + *[load_from_blob_store_if_remote(value) for value in values] + ) + new_args.append(dict(zip(keys, values))) + + elif isinstance(arg, BaseRemoteModel): + new_args.append(await arg.unload_all()) + + elif isinstance(arg, BaseModel): + for field in arg.model_fields.keys(): + if isinstance(getattr(arg, field), RemoteObject): + setattr( + arg, + field, + await load_from_blob_store_if_remote( + getattr(arg, field) + ), + ) + elif isinstance(getattr(arg, field), list): + setattr( + arg, + field, + await asyncio.gather( + *[ + load_from_blob_store_if_remote(item) + for item in getattr(arg, field) + ] + ), + ) + elif isinstance(getattr(arg, field), BaseRemoteModel): + setattr( + arg, + field, + await getattr(arg, field).unload_all(), + ) + + new_args.append(arg) + + else: + new_args.append(arg) + + new_kwargs = {} + + for k, v in kwargs.items(): + if isinstance(v, list): + new_kwargs[k] = await asyncio.gather( + *[load_from_blob_store_if_remote(item) for item in v] + ) + + elif isinstance(v, dict): + keys, values = list(zip(*v.items())) or ([], []) + values = await asyncio.gather( + *[load_from_blob_store_if_remote(value) for value in values] + ) + new_kwargs[k] = dict(zip(keys, values)) + + elif isinstance(v, BaseRemoteModel): + new_kwargs[k] = await v.unload_all() + + elif isinstance(v, BaseModel): + for field in v.model_fields.keys(): + if isinstance(getattr(v, field), RemoteObject): + setattr( + v, + field, + await load_from_blob_store_if_remote( + getattr(v, field) + ), + ) + elif isinstance(getattr(v, field), list): + setattr( + v, + field, + await asyncio.gather( + *[ + load_from_blob_store_if_remote(item) + for item in getattr(v, field) + ] + ), + ) + elif isinstance(getattr(v, field), BaseRemoteModel): + setattr( + v, + field, + await getattr(v, field).unload_all(), + ) + new_kwargs[k] = v + + else: + new_kwargs[k] = v + + return new_args, new_kwargs + + async def unload_return_value(x: Any | BaseRemoteModel) -> Any: + if isinstance(x, BaseRemoteModel): + await x.unload_all() + + return await store_in_blob_store_if_large(x) + + @wraps(f) + async def async_wrapper(*args, **kwargs) -> Any: + new_args, new_kwargs = await load_args(args, kwargs) + output = await f(*new_args, **new_kwargs) + + return await unload_return_value(output) + + return async_wrapper if use_blob_store_for_temporal else f + + return auto_blob_store_decorator(f) if f else auto_blob_store_decorator + + +def auto_blob_store_workflow(f: Callable) -> Callable: + @wraps(f) + async def wrapper(*args, **kwargs) -> Any: + keys = kwargs.keys() + values = [kwargs[k] for k in keys] + + loaded = await workflow.execute_activity( + load_inputs_remote, + args=[[*args, *values]], + schedule_to_close_timeout=timedelta( + seconds=60 if debug or testing else temporal_schedule_to_close_timeout + ), + retry_policy=DEFAULT_RETRY_POLICY, + ) + + loaded_args = loaded[: len(args)] + loaded_kwargs = dict(zip(keys, loaded[len(args) :])) + + result = await f(*loaded_args, **loaded_kwargs) + + return result + + return wrapper if use_blob_store_for_temporal else f diff --git a/agents-api/agents_api/common/utils/messages.py b/agents-api/agents_api/common/utils/messages.py index c94808324..53edbd846 100644 --- a/agents-api/agents_api/common/utils/messages.py +++ b/agents-api/agents_api/common/utils/messages.py @@ -1,26 +1,22 @@ import json from typing import cast +from beartype import beartype + from ...autogen.openapi_model import ( ChatMLImageContentPart, ChatMLTextContentPart, ) +@beartype def content_to_json( - content: str | list[ChatMLTextContentPart] | list[ChatMLImageContentPart] | dict, -): - result = [] + content: str | list[dict] | dict, +) -> list[dict]: if isinstance(content, str): result = [{"type": "text", "text": content}] elif isinstance(content, list): - for part in content: - m = {"type": part.type, part.type: None} - if isinstance(part, ChatMLTextContentPart): - m[part.type] = part.text - elif isinstance(part, ChatMLImageContentPart): - m[part.type] = part.image_url.model_dump() - result.append(m) + result = content elif isinstance(content, dict): result = [{"type": "text", "text": json.dumps(content, indent=4)}] diff --git a/agents-api/agents_api/common/utils/template.py b/agents-api/agents_api/common/utils/template.py index 613e0a647..5bde8cab6 100644 --- a/agents-api/agents_api/common/utils/template.py +++ b/agents-api/agents_api/common/utils/template.py @@ -1,15 +1,13 @@ -import json import re from typing import List, TypeVar -import arrow -import re2 -import yaml from beartype import beartype from jinja2.sandbox import ImmutableSandboxedEnvironment from jinja2schema import infer, to_json_schema from jsonschema import validate +from ...activities.utils import ALLOWED_FUNCTIONS, constants, stdlib + __all__: List[str] = [ "render_template", ] @@ -26,17 +24,8 @@ # Add arrow to jinja -jinja_env.globals["dump_yaml"] = yaml.dump -jinja_env.globals["match_regex"] = lambda pattern, string: bool( - re2.fullmatch(pattern, string) -) -jinja_env.globals["search_regex"] = lambda pattern, string: re2.search(pattern, string) -jinja_env.globals["dump_json"] = json.dumps -jinja_env.globals["arrow"] = arrow -jinja_env.globals["true"] = True -jinja_env.globals["false"] = False -jinja_env.globals["null"] = None - +for k, v in (constants | stdlib | ALLOWED_FUNCTIONS).items(): + jinja_env.globals[k] = v simple_jinja_regex = re.compile(r"{{|{%.+}}|%}", re.DOTALL) diff --git a/agents-api/agents_api/common/utils/yaml.py b/agents-api/agents_api/common/utils/yaml.py new file mode 100644 index 000000000..1cde64aa4 --- /dev/null +++ b/agents-api/agents_api/common/utils/yaml.py @@ -0,0 +1,11 @@ +from typing import Any + +import yaml + + +def load(string: str) -> Any: + return yaml.load(string, Loader=yaml.CSafeLoader) + + +def dump(value: Any) -> str: + return yaml.dump(value, Dumper=yaml.CSafeDumper) diff --git a/agents-api/agents_api/dependencies/query_filter.py b/agents-api/agents_api/dependencies/query_filter.py index c100f1489..73e099225 100644 --- a/agents-api/agents_api/dependencies/query_filter.py +++ b/agents-api/agents_api/dependencies/query_filter.py @@ -1,6 +1,7 @@ -from typing import Any, Callable +from typing import Annotated, Any, Callable -from fastapi import Request +from fastapi import Query, Request +from pydantic import BaseModel, ConfigDict def convert_value(value: str) -> Any: @@ -15,9 +16,13 @@ def convert_value(value: str) -> Any: return value +class MetadataFilter(BaseModel): + model_config = ConfigDict(extra="allow") + + def create_filter_extractor( - prefix: str = "filter", -) -> Callable[[Request], dict[str, Any]]: + prefix: str = "metadata_filter", +) -> Callable[[Request, MetadataFilter], MetadataFilter]: """ Creates a dependency function to extract filter parameters with a given prefix. @@ -31,7 +36,12 @@ def create_filter_extractor( # Add a dot to the prefix to allow for nested filters prefix += "." - def extract_filters(request: Request) -> dict[str, Any]: + def extract_filters( + request: Request, + metadata_filter: Annotated[ + MetadataFilter, Query(default_factory=MetadataFilter) + ], + ) -> MetadataFilter: """ Extracts query parameters that start with the specified prefix and returns them as a dictionary. @@ -49,6 +59,6 @@ def extract_filters(request: Request) -> dict[str, Any]: filter_key = key[len(prefix) :] filters[filter_key] = convert_value(value) - return filters + return MetadataFilter(**filters) return extract_filters diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 2b016fd40..f2d027e27 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -12,6 +12,12 @@ # Initialize the Env object for environment variable parsing. env: Any = Env() +# Debug +# ----- +debug: bool = env.bool("AGENTS_API_DEBUG", default=False) +testing: bool = env.bool("AGENTS_API_TESTING", default=False) +sentry_dsn: str = env.str("SENTRY_DSN", default=None) + # App # --- multi_tenant_mode: bool = env.bool("AGENTS_API_MULTI_TENANT_MODE", default=False) @@ -20,17 +26,22 @@ public_port: int = env.int("AGENTS_API_PUBLIC_PORT", default=80) api_prefix: str = env.str("AGENTS_API_PREFIX", default="") - # Tasks # ----- task_max_parallelism: int = env.int("AGENTS_API_TASK_MAX_PARALLELISM", default=100) -# Debug -# ----- -debug: bool = env.bool("AGENTS_API_DEBUG", default=False) -testing: bool = env.bool("AGENTS_API_TESTING", default=False) -sentry_dsn: str = env.str("SENTRY_DSN", default=None) +# Blob Store +# ---------- +use_blob_store_for_temporal: bool = ( + env.bool("USE_BLOB_STORE_FOR_TEMPORAL", default=False) if not testing else False +) + +blob_store_bucket: str = env.str("BLOB_STORE_BUCKET", default="agents-api") +blob_store_cutoff_kb: int = env.int("BLOB_STORE_CUTOFF_KB", default=64) +s3_endpoint: str = env.str("S3_ENDPOINT", default="http://seaweedfs:8333") +s3_access_key: str | None = env.str("S3_ACCESS_KEY", default=None) +s3_secret_key: str | None = env.str("S3_SECRET_KEY", default=None) # Cozo @@ -56,6 +67,8 @@ api_key_header_name: str = env.str("AGENTS_API_KEY_HEADER_NAME", default="X-Auth-Key") +max_free_sessions: int = env.int("MAX_FREE_SESSIONS", default=50) +max_free_executions: int = env.int("MAX_FREE_EXECUTIONS", default=50) # Litellm API # ----------- @@ -87,7 +100,9 @@ temporal_private_key: str = env.str("TEMPORAL_PRIVATE_KEY", default=None) temporal_endpoint: Any = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") temporal_task_queue: Any = env.str("TEMPORAL_TASK_QUEUE", default="julep-task-queue") - +temporal_schedule_to_close_timeout: int = env.int( + "TEMPORAL_SCHEDULE_TO_CLOSE_TIMEOUT", default=3600 +) # Consolidate environment variables environment: Dict[str, Any] = dict( @@ -105,6 +120,12 @@ temporal_worker_url=temporal_worker_url, temporal_namespace=temporal_namespace, embedding_model_id=embedding_model_id, + use_blob_store_for_temporal=use_blob_store_for_temporal, + blob_store_bucket=blob_store_bucket, + blob_store_cutoff_kb=blob_store_cutoff_kb, + s3_endpoint=s3_endpoint, + s3_access_key=s3_access_key, + s3_secret_key=s3_secret_key, testing=testing, ) diff --git a/agents-api/notebooks/04-remove-messages.ipynb b/agents-api/agents_api/metrics/__init__.py similarity index 100% rename from agents-api/notebooks/04-remove-messages.ipynb rename to agents-api/agents_api/metrics/__init__.py diff --git a/agents-api/agents_api/metrics/counters.py b/agents-api/agents_api/metrics/counters.py new file mode 100644 index 000000000..f80236bf7 --- /dev/null +++ b/agents-api/agents_api/metrics/counters.py @@ -0,0 +1,31 @@ +import inspect +from functools import wraps +from typing import Awaitable, Callable, ParamSpec, TypeVar + +from prometheus_client import Counter + +P = ParamSpec("P") +T = TypeVar("T") + + +def increase_counter(metric_label: str, id_field_name: str = "developer_id"): + def decor(func: Callable[P, T | Awaitable[T]]): + metric = Counter( + metric_label, + f"Number of {metric_label} calls", + labelnames=(id_field_name,), + ) + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + metric.labels(kwargs.get(id_field_name, "not_set")).inc() + return func(*args, **kwargs) + + @wraps(func) + async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + metric.labels(kwargs.get(id_field_name, "not_set")).inc() + return await func(*args, **kwargs) + + return async_wrapper if inspect.iscoroutinefunction(func) else wrapper + + return decor diff --git a/agents-api/agents_api/models/__init__.py b/agents-api/agents_api/models/__init__.py index b1f918ee7..e59b5b01c 100644 --- a/agents-api/agents_api/models/__init__.py +++ b/agents-api/agents_api/models/__init__.py @@ -8,12 +8,13 @@ # ruff: noqa: F401, F403, F405 -import agents_api.models.agent as agent -import agents_api.models.developer as developer -import agents_api.models.docs as docs -import agents_api.models.entry as entry -import agents_api.models.execution as execution -import agents_api.models.session as session -import agents_api.models.task as task -import agents_api.models.tools as tools -import agents_api.models.user as user +from . import agent as agent +from . import developer as developer +from . import docs as docs +from . import entry as entry +from . import execution as execution +from . import files as files +from . import session as session +from . import task as task +from . import tools as tools +from . import user as user diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 98daab540..a9f0bfb8f 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -13,6 +13,7 @@ from ...autogen.openapi_model import Agent, CreateAgentRequest from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -57,6 +58,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_agent") @beartype def create_agent( *, @@ -88,7 +90,7 @@ def create_agent( else [data.instructions] ) - agent_data = data.model_dump(exclude_unset=True) + agent_data = data.model_dump() default_settings = agent_data.pop("default_settings") settings_cols, settings_vals = cozo_process_mutate_data( diff --git a/agents-api/agents_api/models/agent/create_or_update_agent.py b/agents-api/agents_api/models/agent/create_or_update_agent.py index 8a3975183..9a1feb717 100644 --- a/agents-api/agents_api/models/agent/create_or_update_agent.py +++ b/agents-api/agents_api/models/agent/create_or_update_agent.py @@ -13,6 +13,7 @@ from ...autogen.openapi_model import Agent, CreateOrUpdateAgentRequest from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -48,6 +49,7 @@ Agent, one=True, transform=lambda d: {"id": UUID(d.pop("agent_id")), **d} ) @cozo_query +@increase_counter("create_or_update_agent") @beartype def create_or_update_agent( *, @@ -83,7 +85,11 @@ def create_or_update_agent( data.default_settings = data.default_settings or {} agent_data = data.model_dump() - default_settings = agent_data.pop("default_settings") + default_settings = ( + data.default_settings.model_dump(exclude_none=True) + if data.default_settings + else {} + ) settings_cols, settings_vals = cozo_process_mutate_data( { diff --git a/agents-api/agents_api/models/agent/patch_agent.py b/agents-api/agents_api/models/agent/patch_agent.py index 364d1a974..99d4e3553 100644 --- a/agents-api/agents_api/models/agent/patch_agent.py +++ b/agents-api/agents_api/models/agent/patch_agent.py @@ -9,6 +9,7 @@ from ...autogen.openapi_model import PatchAgentRequest, ResourceUpdatedResponse from ...common.utils.cozo import cozo_process_mutate_data from ...common.utils.datetime import utcnow +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -36,6 +37,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("patch_agent") @beartype def patch_agent( *, diff --git a/agents-api/agents_api/models/agent/update_agent.py b/agents-api/agents_api/models/agent/update_agent.py index 992bb9796..b36f687eb 100644 --- a/agents-api/agents_api/models/agent/update_agent.py +++ b/agents-api/agents_api/models/agent/update_agent.py @@ -8,6 +8,7 @@ from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateAgentRequest from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -35,6 +36,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("update_agent") @beartype def update_agent( *, diff --git a/agents-api/agents_api/models/chat/gather_messages.py b/agents-api/agents_api/models/chat/gather_messages.py index b6bfdc217..ea3b4a21e 100644 --- a/agents-api/agents_api/models/chat/gather_messages.py +++ b/agents-api/agents_api/models/chat/gather_messages.py @@ -6,12 +6,11 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.Chat import ChatInput -from ...autogen.openapi_model import DocReference, History +from ...autogen.openapi_model import ChatInput, DocReference, History from ...clients import litellm from ...common.protocol.developers import Developer from ...common.protocol.sessions import ChatContext -from ..docs.search_docs_hybrid import search_docs_hybrid +from ..docs import search_docs_by_embedding from ..entry.get_history import get_history from ..utils import ( partialclass, @@ -35,8 +34,8 @@ async def gather_messages( session_id: UUID, chat_context: ChatContext, chat_input: ChatInput, -): - new_raw_messages = [msg.model_dump() for msg in chat_input.messages] +) -> tuple[list[dict], list[DocReference]]: + new_raw_messages = [msg.model_dump(mode="json") for msg in chat_input.messages] recall = chat_input.recall assert len(new_raw_messages) > 0 @@ -51,35 +50,71 @@ async def gather_messages( # Keep leaf nodes only relations = history.relations past_messages = [ - entry.model_dump() + entry.model_dump(mode="json") for entry in history.entries if entry.id not in {r.head for r in relations} ] + # Collapse the message content if content is a list of strings and only one string + for message in past_messages: + if ( + isinstance(message["content"], list) + and len(message["content"]) == 1 + and message["content"][0].get("type") == "text" + ): + message["content"] = message["content"][0]["text"].strip() + if not recall: return past_messages, [] + # TODO: Make this configurable? + search_threshold = 4 + search_query_chars = 1000 + search_messages = [ + msg + for msg in (past_messages + new_raw_messages)[-(search_threshold):] + if isinstance(msg["content"], str) and msg["role"] in ["user", "assistant"] + ] + + if len(search_messages) == 0: + return past_messages, [] + + # FIXME: This should only search text messages and not embed if text is empty # Search matching docs + embed_text = "\n\n".join( + [ + f"{msg.get('name') or msg['role']}: {msg['content']}" + for msg in search_messages + ] + ).strip() + [query_embedding, *_] = await litellm.aembedding( - inputs="\n\n".join( - [ - f"{msg.get('name') or msg['role']}: {msg['content']}" - for msg in new_raw_messages - ] - ), + # Truncate on the left to keep the last `search_query_chars` characters + inputs=embed_text[-(search_query_chars):], ) - query_text = new_raw_messages[-1]["content"] + + # Truncate on the right to take only the first `search_query_chars` characters + search_messages[-1]["content"].strip()[:search_query_chars] # List all the applicable owners to search docs from active_agent_id = chat_context.get_active_agent().id user_ids = [user.id for user in chat_context.users] owners = [("user", user_id) for user_id in user_ids] + [("agent", active_agent_id)] - doc_references: list[DocReference] = search_docs_hybrid( + # TODO: Hybrid search is timing out when there are a lot of docs, so now + # sticking to embedding search only. Need to add a search_type option that + # controls which search to use. See: https://github.com/cozodb/cozo/issues/106 + doc_references: list[DocReference] = search_docs_by_embedding( developer_id=developer.id, owners=owners, - query=query_text, query_embedding=query_embedding, ) + # doc_references: list[DocReference] = search_docs_hybrid( + # developer_id=developer.id, + # owners=owners, + # query=query_text, + # query_embedding=query_embedding, + # ) + return past_messages, doc_references diff --git a/agents-api/agents_api/models/docs/create_doc.py b/agents-api/agents_api/models/docs/create_doc.py index 1c667bd51..3b9c8c9f7 100644 --- a/agents-api/agents_api/models/docs/create_doc.py +++ b/agents-api/agents_api/models/docs/create_doc.py @@ -8,6 +8,7 @@ from ...autogen.openapi_model import CreateDocRequest, Doc from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -37,6 +38,7 @@ }, ) @cozo_query +@increase_counter("create_doc") @beartype def create_doc( *, @@ -52,7 +54,7 @@ def create_doc( Parameters: owner_type (Literal["user", "agent"]): The type of the owner of the document. owner_id (UUID): The UUID of the document owner. - id (UUID): The UUID of the document to be created. + doc_id (UUID): The UUID of the document to be created. data (CreateDocRequest): The content of the document. """ @@ -65,6 +67,7 @@ def create_doc( data.metadata = data.metadata or {} doc_data = data.model_dump() + doc_data.pop("embed_instruction", None) content = doc_data.pop("content") doc_data["owner_type"] = owner_type diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py index e81084985..d47cc80a8 100644 --- a/agents-api/agents_api/models/docs/get_doc.py +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -37,7 +37,11 @@ one=True, transform=lambda d: { "content": [s[1] for s in sorted(d["snippet_data"], key=lambda x: x[0])], - "embeddings": [s[2] for s in sorted(d["snippet_data"], key=lambda x: x[0])], + "embeddings": [ + s[2] + for s in sorted(d["snippet_data"], key=lambda x: x[0]) + if s[2] is not None + ], **d, }, ) diff --git a/agents-api/agents_api/models/docs/list_docs.py b/agents-api/agents_api/models/docs/list_docs.py index 4dad7ec06..dd389d58c 100644 --- a/agents-api/agents_api/models/docs/list_docs.py +++ b/agents-api/agents_api/models/docs/list_docs.py @@ -34,7 +34,11 @@ Doc, transform=lambda d: { "content": [s[1] for s in sorted(d["snippet_data"], key=lambda x: x[0])], - "embeddings": [s[2] for s in sorted(d["snippet_data"], key=lambda x: x[0])], + "embeddings": [ + s[2] + for s in sorted(d["snippet_data"], key=lambda x: x[0]) + if s[2] is not None + ], **d, }, ) @@ -50,7 +54,26 @@ def list_docs( sort_by: Literal["created_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", metadata_filter: dict[str, Any] = {}, + include_without_embeddings: bool = False, ) -> tuple[list[str], dict]: + """ + Constructs and returns a datalog query for listing documents and their associated information snippets. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the documents. + owner_id (UUID): The unique identifier of the owner (user or agent) associated with the documents. + owner_type (Literal["user", "agent"]): The type of owner associated with the documents. + limit (int): The maximum number of documents to return. + offset (int): The number of documents to skip before returning the results. + sort_by (Literal["created_at"]): The field to sort the documents by. + direction (Literal["asc", "desc"]): The direction to sort the documents in. + metadata_filter (dict): A dictionary of metadata filters to apply to the documents. + include_without_embeddings (bool): Whether to include documents without embeddings in the results. + + Returns: + Doc[] + """ + # Transforms the metadata_filter dictionary into a string representation for the datalog query. metadata_filter_str = ", ".join( [ @@ -70,6 +93,7 @@ def list_docs( content, embedding, }}, + {"" if include_without_embeddings else "not is_null(embedding),"} snippet_data = [index, content, embedding] ?[ diff --git a/agents-api/agents_api/models/docs/mmr.py b/agents-api/agents_api/models/docs/mmr.py new file mode 100644 index 000000000..d214e8c04 --- /dev/null +++ b/agents-api/agents_api/models/docs/mmr.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +import logging +from typing import Union + +import numpy as np + +Matrix = Union[list[list[float]], list[np.ndarray], np.ndarray] + +logger = logging.getLogger(__name__) + + +def _cosine_similarity(x: Matrix, y: Matrix) -> np.ndarray: + """Row-wise cosine similarity between two equal-width matrices. + + Args: + x: A matrix of shape (n, m). + y: A matrix of shape (k, m). + + Returns: + A matrix of shape (n, k) where each element (i, j) is the cosine similarity + between the ith row of X and the jth row of Y. + + Raises: + ValueError: If the number of columns in X and Y are not the same. + ImportError: If numpy is not installed. + """ + + if len(x) == 0 or len(y) == 0: + return np.array([]) + + x = [xx for xx in x if xx is not None] + y = [yy for yy in y if yy is not None] + + x = np.array(x) + y = np.array(y) + if x.shape[1] != y.shape[1]: + msg = ( + f"Number of columns in X and Y must be the same. X has shape {x.shape} " + f"and Y has shape {y.shape}." + ) + raise ValueError(msg) + try: + import simsimd as simd # type: ignore + + x = np.array(x, dtype=np.float32) + y = np.array(y, dtype=np.float32) + z = 1 - np.array(simd.cdist(x, y, metric="cosine")) + return z + except ImportError: + logger.debug( + "Unable to import simsimd, defaulting to NumPy implementation. If you want " + "to use simsimd please install with `pip install simsimd`." + ) + x_norm = np.linalg.norm(x, axis=1) + y_norm = np.linalg.norm(y, axis=1) + # Ignore divide by zero errors run time warnings as those are handled below. + with np.errstate(divide="ignore", invalid="ignore"): + similarity = np.dot(x, y.T) / np.outer(x_norm, y_norm) + similarity[np.isnan(similarity) | np.isinf(similarity)] = 0.0 + return similarity + + +def maximal_marginal_relevance( + query_embedding: np.ndarray, + embedding_list: list, + lambda_mult: float = 0.5, + k: int = 4, +) -> list[int]: + """Calculate maximal marginal relevance. + + Args: + query_embedding: The query embedding. + embedding_list: A list of embeddings. + lambda_mult: The lambda parameter for MMR. Default is 0.5. + k: The number of embeddings to return. Default is 4. + + Returns: + A list of indices of the embeddings to return. + + Raises: + ImportError: If numpy is not installed. + """ + + if min(k, len(embedding_list)) <= 0: + return [] + if query_embedding.ndim == 1: + query_embedding = np.expand_dims(query_embedding, axis=0) + similarity_to_query = _cosine_similarity(query_embedding, embedding_list)[0] + most_similar = int(np.argmax(similarity_to_query)) + idxs = [most_similar] + selected = np.array([embedding_list[most_similar]]) + while len(idxs) < min(k, len(embedding_list)): + best_score = -np.inf + idx_to_add = -1 + similarity_to_selected = _cosine_similarity(embedding_list, selected) + for i, query_score in enumerate(similarity_to_query): + if i in idxs: + continue + redundant_score = max(similarity_to_selected[i]) + equation_score = ( + lambda_mult * query_score - (1 - lambda_mult) * redundant_score + ) + if equation_score > best_score: + best_score = equation_score + idx_to_add = i + idxs.append(idx_to_add) + selected = np.append(selected, [embedding_list[idx_to_add]], axis=0) + return idxs diff --git a/agents-api/agents_api/models/docs/search_docs_by_embedding.py b/agents-api/agents_api/models/docs/search_docs_by_embedding.py index 83418aa21..992e12f9d 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_embedding.py +++ b/agents-api/agents_api/models/docs/search_docs_by_embedding.py @@ -1,5 +1,6 @@ """This module contains functions for searching documents in the CozoDB based on embedding queries.""" +import json from typing import Any, Literal, TypeVar from uuid import UUID @@ -36,6 +37,7 @@ "id": d["owner_id"], "role": d["owner_type"], }, + "metadata": d.get("metadata", {}), **d, }, ) @@ -47,11 +49,12 @@ def search_docs_by_embedding( owners: list[tuple[Literal["user", "agent"], UUID]], query_embedding: list[float], k: int = 3, - confidence: float = 0.7, - ef: int = 128, - mmr_lambda: float = 0.25, + confidence: float = 0.5, + ef: int = 50, embedding_size: int = 1024, -) -> tuple[list[str], dict]: + ann_threshold: int = 1_000_000, + metadata_filter: dict[str, Any] = {}, +) -> tuple[str, dict]: """ Searches for document snippets in CozoDB by embedding query. @@ -62,11 +65,20 @@ def search_docs_by_embedding( k (int, optional): The number of nearest neighbors to retrieve. Defaults to 3. confidence (float, optional): The confidence threshold for filtering results. Defaults to 0.8. mmr_lambda (float, optional): The lambda parameter for MMR. Defaults to 0.25. + embedding_size (int): Embedding vector length + metadata_filter (dict[str, Any]): Dictionary to filter agents based on metadata. """ assert len(query_embedding) == embedding_size assert sum(query_embedding) + metadata_filter_str = ", ".join( + [ + f"metadata->{json.dumps(k)} == {json.dumps(v)}" + for k, v in metadata_filter.items() + ] + ) + owners: list[list[str]] = [ [owner_type, str(owner_id)] for owner_type, owner_id in owners ] @@ -74,87 +86,160 @@ def search_docs_by_embedding( # Calculate the search radius based on confidence level radius: float = 1.0 - confidence - # Construct the datalog query for searching document snippets - interim_query = f""" + determine_knn_ann_query = f""" owners[owner_type, owner_id] <- $owners - input[ - owner_type, - owner_id, - query_embedding, - ] := + snippet_counter[count(item)] := owners[owner_type, owner_id_str], owner_id = to_uuid(owner_id_str), - query_embedding = vec($query_embedding) - - candidate[doc_id] := - input[owner_type, owner_id, _], *docs {{ owner_type, owner_id, - doc_id + doc_id: item, + metadata, }} + {', ' + metadata_filter_str if metadata_filter_str.strip() else ''} - intersnippet_distance[ - doc_id, - index1, - min(dist) - ] := - *snippets {{ - doc_id, - index: index1, - embedding: embedding1 - }}, - *snippets {{ + ?[use_ann] := + snippet_counter[count], + count > {ann_threshold}, + use_ann = true + + :limit 1 + :create _determine_knn_ann {{ + use_ann + }} + """ + + # Construct the datalog query for searching document snippets + search_query = f""" + # %debug _determine_knn_ann + %if {{ + ?[use_ann] := *_determine_knn_ann{{ use_ann }} + }} + + %then {{ + owners[owner_type, owner_id] <- $owners + input[ + owner_type, + owner_id, + query_embedding, + ] := + owners[owner_type, owner_id_str], + owner_id = to_uuid(owner_id_str), + query_embedding = vec($query_embedding) + + # Search for documents by owner + ?[ doc_id, - index: index2, - embedding: embedding2 - }}, - index1 < index2, - dist = cos_dist(embedding1, embedding2) - - doclength[doc_id, max(index)] := - *snippets {{ + index, + title, + content, + distance, + embedding, + metadata, + ] := + # Get input values + input[owner_type, owner_id, query], + + # Restrict the search to all documents that match the owner + *docs {{ + owner_type, + owner_id, + doc_id, + title, + metadata, + }}, + + # Search for snippets in the embedding space + ~snippets:embedding_space {{ + doc_id, + index, + content + | + query: query, + k: {k}, + ef: {ef}, + radius: {radius}, + bind_distance: distance, + bind_vector: embedding, + }} + + :sort distance + :limit {k} + + :create _search_result {{ doc_id, index, + title, + content, + distance, + embedding, + metadata, }} + }} - get_intersnippet[doc_id, index, distance] := - intersnippet_distance[doc_id, _, distance] + %else {{ + owners[owner_type, owner_id] <- $owners + input[ + owner_type, + owner_id, + query_embedding, + ] := + owners[owner_type, owner_id_str], + owner_id = to_uuid(owner_id_str), + query_embedding = vec($query_embedding) - get_intersnippet[doc_id, index, distance] := - not intersnippet_distance[doc_id, _, distance], - distance = 0.0 + # Search for documents by owner + ?[ + doc_id, + index, + title, + content, + distance, + embedding, + metadata, + ] := + # Get input values + input[owner_type, owner_id, query], - search_result[ - doc_id, - content, - index, - distance, - ] := - input[_, __, query], - candidate[doc_id], - ~snippets:embedding_space {{ + # Restrict the search to all documents that match the owner + *docs {{ + owner_type, + owner_id, + doc_id, + title, + metadata, + }}, + + # Search for snippets in the embedding space + *snippets {{ + doc_id, + index, + content, + embedding, + }}, + !is_null(embedding), + distance = cos_dist(query, embedding), + distance <= {radius} + + :sort distance + :limit {k} + + :create _search_result {{ doc_id, index, - content - | - query: query, - k: {k*2}, - ef: {ef}, - radius: {radius}, - bind_distance: distance, + title, + content, + distance, + embedding, + metadata, }} + }} + %end + """ - apply_mmr[ - doc_id, - snippet_data, - distance, - mmr_score, - ] := - search_result[doc_id, content, index, distance], - get_intersnippet[doc_id, index, intersnippet_distance], - mmr_score = {mmr_lambda} * (distance - (1.0 - {mmr_lambda}) * intersnippet_distance), - snippet_data = [index, content] + normal_interim_query = f""" + owners[owner_type, owner_id] <- $owners ?[ owner_type, @@ -162,24 +247,16 @@ def search_docs_by_embedding( doc_id, snippet_data, distance, - mmr_score, title, + embedding, + metadata, ] := - *docs {{ - owner_type, - owner_id, - doc_id, - title, - }}, - apply_mmr[ - doc_id, - snippet_data, - distance, - mmr_score, - ] + owners[owner_type, owner_id_str], + owner_id = to_uuid(owner_id_str), + *_search_result{{ doc_id, index, title, content, distance, embedding, metadata }}, + snippet_data = [index, content] - # Sort the results by distance to find the closest matches - :sort -mmr_score + :sort distance :limit {k} :create _interim {{ @@ -188,8 +265,9 @@ def search_docs_by_embedding( doc_id, snippet_data, distance, - mmr_score, title, + embedding, + metadata, }} """ @@ -201,6 +279,8 @@ def search_docs_by_embedding( unique(snippet_data), distance, title, + embedding, + metadata, ] := *_interim { owner_type, @@ -209,15 +289,18 @@ def search_docs_by_embedding( snippet_data, distance, title, + embedding, + metadata, } m[ doc_id, owner_type, owner_id, - collect(snippet), + snippet, distance, title, + metadata, ] := n[ doc_id, @@ -226,10 +309,13 @@ def search_docs_by_embedding( snippet_data, distance, title, + embedding, + metadata, ], snippet = { "index": snippet_datum->0, - "content": snippet_datum->1 + "content": snippet_datum->1, + "embedding": embedding, }, snippet_datum in snippet_data @@ -237,33 +323,45 @@ def search_docs_by_embedding( id, owner_type, owner_id, - snippets, + snippet, distance, title, + metadata, ] := m[ id, owner_type, owner_id, - snippets, + snippet, distance, title, + metadata, ] + + :sort distance """ - queries = [ - verify_developer_id_query(developer_id), - *[ - verify_developer_owns_resource_query( - developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} - ) - for owner_type, owner_id in owners - ], - interim_query, - collect_query, - ] + verify_query = "}\n\n{".join( + [ + verify_developer_id_query(developer_id), + *[ + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ) + for owner_type, owner_id in owners + ], + ] + ) + + query = f""" + {{ {verify_query} }} + {{ {determine_knn_ann_query} }} + {search_query} + {{ {normal_interim_query} }} + {{ {collect_query} }} + """ return ( - queries, + query, { "owners": owners, "query_embedding": query_embedding, diff --git a/agents-api/agents_api/models/docs/search_docs_by_text.py b/agents-api/agents_api/models/docs/search_docs_by_text.py index bb700a494..4d30fec5d 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_text.py +++ b/agents-api/agents_api/models/docs/search_docs_by_text.py @@ -1,6 +1,7 @@ """This module contains functions for searching documents in the CozoDB based on embedding queries.""" import json +import re from typing import Any, Literal, TypeVar from uuid import UUID @@ -10,6 +11,7 @@ from pydantic import ValidationError from ...autogen.openapi_model import DocReference +from ...common.nlp import paragraph_to_custom_queries from ..utils import ( cozo_query, partialclass, @@ -37,6 +39,7 @@ "id": d["owner_id"], "role": d["owner_type"], }, + "metadata": d.get("metadata", {}), **d, }, ) @@ -48,6 +51,7 @@ def search_docs_by_text( owners: list[tuple[Literal["user", "agent"], UUID]], query: str, k: int = 3, + metadata_filter: dict[str, Any] = {}, ) -> tuple[list[str], dict]: """ Searches for document snippets in CozoDB by embedding query. @@ -56,15 +60,23 @@ def search_docs_by_text( owners (list[tuple[Literal["user", "agent"], UUID]]): The type of the owner of the documents. query (str): The query string. k (int, optional): The number of nearest neighbors to retrieve. Defaults to 3. + metadata_filter (dict[str, Any]): Dictionary to filter agents based on metadata. """ + metadata_filter_str = ", ".join( + [ + f"metadata->{json.dumps(k)} == {json.dumps(v)}" + for k, v in metadata_filter.items() + ] + ) owners: list[list[str]] = [ [owner_type, str(owner_id)] for owner_type, owner_id in owners ] - # Need to use NEAR/3($query) to search for arbitrary text within 3 words of each other # See: https://docs.cozodb.org/en/latest/vector.html#full-text-search-fts - query = f"NEAR/3({json.dumps(query)})" + fts_queries = paragraph_to_custom_queries(query) or [ + re.sub(r"[^\w\s\-_]+", "", query) + ] # Construct the datalog query for searching document snippets search_query = f""" @@ -81,8 +93,10 @@ def search_docs_by_text( *docs {{ owner_type, owner_id, - doc_id + doc_id, + metadata, }} + {', ' + metadata_filter_str if metadata_filter_str.strip() else ''} search_result[ doc_id, @@ -112,21 +126,23 @@ def search_docs_by_text( index, content | - query: $query, + query: query, k: {k}, score_kind: 'tf_idf', bind_score: score, }}, + query in $fts_queries, distance = -score, snippet_data = [index, content] m[ doc_id, - collect(snippet), + snippet, distance, title, owner_type, owner_id, + metadata, ] := candidate[doc_id], *docs {{ @@ -134,6 +150,7 @@ def search_docs_by_text( owner_id, doc_id, title, + metadata, }}, search_result [ doc_id, @@ -150,18 +167,21 @@ def search_docs_by_text( id, owner_type, owner_id, - snippets, + snippet, distance, title, + metadata, ] := + candidate[id], input[owner_type, owner_id], m[ id, - snippets, + snippet, distance, title, owner_type, owner_id, + metadata, ] # Sort the results by distance to find the closest matches @@ -182,5 +202,5 @@ def search_docs_by_text( return ( queries, - {"owners": owners, "query": query}, + {"owners": owners, "query": query, "fts_queries": fts_queries}, ) diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index 598600511..d4710aa78 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -1,7 +1,7 @@ """This module contains functions for searching documents in the CozoDB based on embedding queries.""" from statistics import mean, stdev -from typing import Literal +from typing import Any, Literal from uuid import UUID from beartype import beartype @@ -100,22 +100,34 @@ def search_docs_hybrid( alpha: float = 0.7, # Weight of the embedding search results (this is a good default) embed_search_options: dict = {}, text_search_options: dict = {}, + metadata_filter: dict[str, Any] = {}, ) -> list[DocReference]: # TODO: We should probably parallelize these queries - text_results = search_docs_by_text( - developer_id=developer_id, - owners=owners, - query=query, - k=2 * k, - **text_search_options, + + text_results = ( + search_docs_by_text( + developer_id=developer_id, + owners=owners, + query=query, + k=k, + metadata_filter=metadata_filter, + **text_search_options, + ) + if bool(query.strip()) + else [] ) - embedding_results = search_docs_by_embedding( - developer_id=developer_id, - owners=owners, - query_embedding=query_embedding, - k=2 * k, - **embed_search_options, + embedding_results = ( + search_docs_by_embedding( + developer_id=developer_id, + owners=owners, + query_embedding=query_embedding, + k=k, + metadata_filter=metadata_filter, + **embed_search_options, + ) + if bool(sum(query_embedding)) + else [] ) return dbsf_fuse(text_results, embedding_results, alpha)[:k] diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index e227714d1..a8671a6dd 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -10,6 +10,7 @@ from ...common.utils.cozo import cozo_process_mutate_data from ...common.utils.datetime import utcnow from ...common.utils.messages import content_to_json +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, mark_session_updated_query, @@ -40,6 +41,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_entries") @beartype def create_entries( *, @@ -51,10 +53,10 @@ def create_entries( developer_id = str(developer_id) session_id = str(session_id) - data_dicts = [item.model_dump(exclude_unset=True) for item in data] + data_dicts = [item.model_dump(mode="json") for item in data] for item in data_dicts: - item["content"] = content_to_json(item["content"]) + item["content"] = content_to_json(item["content"] or []) item["session_id"] = session_id item["entry_id"] = item.pop("id", None) or str(uuid4()) item["created_at"] = (item.get("created_at") or utcnow()).timestamp() diff --git a/agents-api/agents_api/models/entry/get_history.py b/agents-api/agents_api/models/entry/get_history.py index bd3c0397a..4be23804e 100644 --- a/agents-api/agents_api/models/entry/get_history.py +++ b/agents-api/agents_api/models/entry/get_history.py @@ -71,7 +71,9 @@ def get_history( token_count, tokenizer, created_at, + tool_calls, timestamp, + tool_call_id, }, source in $allowed_sources, session_id = to_uuid($session_id), @@ -85,7 +87,9 @@ def get_history( "token_count": token_count, "tokenizer": tokenizer, "created_at": created_at, - "timestamp": timestamp + "timestamp": timestamp, + "tool_calls": tool_calls, + "tool_call_id": tool_call_id, } session_relations[unique(item)] := diff --git a/agents-api/agents_api/models/execution/__init__.py b/agents-api/agents_api/models/execution/__init__.py index 7de8e16c5..abd3c7e47 100644 --- a/agents-api/agents_api/models/execution/__init__.py +++ b/agents-api/agents_api/models/execution/__init__.py @@ -1,7 +1,11 @@ # ruff: noqa: F401, F403, F405 +from .count_executions import count_executions from .create_execution import create_execution -from .create_execution_transition import create_execution_transition +from .create_execution_transition import ( + create_execution_transition, + create_execution_transition_async, +) from .get_execution import get_execution from .get_execution_transition import get_execution_transition from .list_execution_transitions import list_execution_transitions diff --git a/agents-api/agents_api/models/execution/constants.py b/agents-api/agents_api/models/execution/constants.py new file mode 100644 index 000000000..8d4568ba2 --- /dev/null +++ b/agents-api/agents_api/models/execution/constants.py @@ -0,0 +1,5 @@ +########## +# Consts # +########## + +OUTPUT_UNNEST_KEY = "$$e7w_unnest$$" diff --git a/agents-api/agents_api/models/execution/count_executions.py b/agents-api/agents_api/models/execution/count_executions.py new file mode 100644 index 000000000..7f10e5bfa --- /dev/null +++ b/agents-api/agents_api/models/execution/count_executions.py @@ -0,0 +1,61 @@ +from typing import Any, TypeVar +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class(dict, one=True) +@cozo_query +@beartype +def count_executions( + *, + developer_id: UUID, + task_id: UUID, +) -> tuple[list[str], dict]: + count_query = """ + input[task_id] <- [[to_uuid($task_id)]] + + counter[count(id)] := + input[task_id], + *executions { + task_id, + execution_id: id, + } + + ?[count] := counter[count] + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, + "tasks", + task_id=task_id, + parents=[("agents", "agent_id")], + ), + count_query, + ] + + return (queries, {"task_id": str(task_id)}) diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index b4918065b..832532d6d 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -9,6 +9,7 @@ from ...autogen.openapi_model import CreateExecutionRequest, Execution from ...common.utils.cozo import cozo_process_mutate_data from ...common.utils.types import dict_like +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -17,6 +18,7 @@ verify_developer_owns_resource_query, wrap_in_class, ) +from .constants import OUTPUT_UNNEST_KEY ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -36,6 +38,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_execution") @beartype def create_execution( *, @@ -57,6 +60,11 @@ def create_execution( data["metadata"] = data.get("metadata", {}) execution_data = data + if execution_data["output"] is not None and not isinstance( + execution_data["output"], dict + ): + execution_data["output"] = {OUTPUT_UNNEST_KEY: execution_data["output"]} + columns, values = cozo_process_mutate_data( { **execution_data, diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index f40395126..d885379fb 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -12,8 +12,10 @@ ) from ...common.protocol.tasks import transition_to_execution_status, valid_transitions from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, + cozo_query_async, partialclass, rewrap_exceptions, verify_developer_id_query, @@ -29,9 +31,13 @@ def validate_transition_targets(data: CreateTransitionRequest) -> None: case "finish_branch": pass # TODO: Implement case "finish" | "error" | "cancelled": - assert ( - data.next is None - ), "Next target must be None for finish/finish_branch/error/cancelled" + pass + + ### FIXME: HACK: Fix this and uncomment + + ### assert ( + ### data.next is None + ### ), "Next target must be None for finish/finish_branch/error/cancelled" case "init_branch" | "init": assert ( @@ -72,6 +78,7 @@ def validate_transition_targets(data: CreateTransitionRequest) -> None: _kind="inserted", ) @cozo_query +@increase_counter("create_execution_transition") @beartype def create_execution_transition( *, @@ -89,6 +96,182 @@ def create_execution_transition( data.metadata = data.metadata or {} data.execution_id = execution_id + # Dump to json + if isinstance(data.output, list): + data.output = [ + item.model_dump(mode="json") if hasattr(item, "model_dump") else item + for item in data.output + ] + + elif hasattr(data.output, "model_dump"): + data.output = data.output.model_dump(mode="json") + + # TODO: This is a hack to make sure the transition is valid + # (parallel transitions are whack, we should do something better) + is_parallel = data.current.workflow.startswith("PAR:") + + # Prepare the transition data + transition_data = data.model_dump(exclude_unset=True, exclude={"id"}) + + # Parse the current and next targets + validate_transition_targets(data) + current_target = transition_data.pop("current") + next_target = transition_data.pop("next") + + transition_data["current"] = (current_target["workflow"], current_target["step"]) + transition_data["next"] = next_target and ( + next_target["workflow"], + next_target["step"], + ) + + columns, transition_values = cozo_process_mutate_data( + { + **transition_data, + "task_token": str(task_token), # Converting to str for JSON serialisation + "transition_id": str(transition_id), + "execution_id": str(execution_id), + } + ) + + # Make sure the transition is valid + check_last_transition_query = f""" + valid_transition[start, end] <- [ + {", ".join(f'["{start}", "{end}"]' for start, ends in valid_transitions.items() for end in ends)} + ] + + last_transition_type[min_cost(type_created_at)] := + *transitions {{ + execution_id: to_uuid("{str(execution_id)}"), + type, + created_at, + }}, + type_created_at = [type, -created_at] + + matched[collect(last_type)] := + last_transition_type[data], + last_type_data = first(data), + last_type = if(is_null(last_type_data), "init", last_type_data), + valid_transition[last_type, $next_type] + + ?[valid] := + matched[prev_transitions], + found = length(prev_transitions), + valid = if($next_type == "init", found == 0, found > 0), + assert(valid, "Invalid transition"), + + :limit 1 + """ + + # Prepare the insert query + insert_query = f""" + ?[{columns}] <- $transition_values + + :insert transitions {{ + {columns} + }} + + :returning + """ + + validate_status_query, update_execution_query, update_execution_params = ( + "", + "", + {}, + ) + + if update_execution_status: + assert ( + task_id is not None + ), "task_id is required for updating the execution status" + + # Prepare the execution update query + [*_, validate_status_query, update_execution_query], update_execution_params = ( + update_execution.__wrapped__( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data=UpdateExecutionRequest( + status=transition_to_execution_status[data.type] + ), + output=data.output if data.type != "error" else None, + error=str(data.output) + if data.type == "error" and data.output + else None, + ) + ) + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, + "executions", + execution_id=execution_id, + parents=[("agents", "agent_id"), ("tasks", "task_id")], + ), + validate_status_query if not is_parallel else None, + update_execution_query if not is_parallel else None, + check_last_transition_query if not is_parallel else None, + insert_query, + ] + + return ( + queries, + { + "transition_values": transition_values, + "next_type": data.type, + "valid_transitions": valid_transitions, + **update_execution_params, + }, + ) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + Transition, + transform=lambda d: { + **d, + "id": d["transition_id"], + "current": {"workflow": d["current"][0], "step": d["current"][1]}, + "next": d["next"] and {"workflow": d["next"][0], "step": d["next"][1]}, + }, + one=True, + _kind="inserted", +) +@cozo_query_async +@increase_counter("create_execution_transition_async") +@beartype +async def create_execution_transition_async( + *, + developer_id: UUID, + execution_id: UUID, + data: CreateTransitionRequest, + # Only one of these needed + transition_id: UUID | None = None, + task_token: str | None = None, + # Only required for updating the execution status as well + update_execution_status: bool = False, + task_id: UUID | None = None, +) -> tuple[list[str | None], dict]: + transition_id = transition_id or uuid4() + data.metadata = data.metadata or {} + data.execution_id = execution_id + + # Dump to json + if isinstance(data.output, list): + data.output = [ + item.model_dump(mode="json") if hasattr(item, "model_dump") else item + for item in data.output + ] + + elif hasattr(data.output, "model_dump"): + data.output = data.output.model_dump(mode="json") + # TODO: This is a hack to make sure the transition is valid # (parallel transitions are whack, we should do something better) is_parallel = data.current.workflow.startswith("PAR:") @@ -176,7 +359,7 @@ def create_execution_transition( data=UpdateExecutionRequest( status=transition_to_execution_status[data.type] ), - output=data.output if data.type == "finish" else None, + output=data.output if data.type != "error" else None, error=str(data.output) if data.type == "error" and data.output else None, diff --git a/agents-api/agents_api/models/execution/create_temporal_lookup.py b/agents-api/agents_api/models/execution/create_temporal_lookup.py index ddc6430c2..e47a505db 100644 --- a/agents-api/agents_api/models/execution/create_temporal_lookup.py +++ b/agents-api/agents_api/models/execution/create_temporal_lookup.py @@ -8,6 +8,7 @@ from temporalio.client import WorkflowHandle from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -28,6 +29,7 @@ } ) @cozo_query +@increase_counter("create_temporal_lookup") @beartype def create_temporal_lookup( *, diff --git a/agents-api/agents_api/models/execution/get_execution.py b/agents-api/agents_api/models/execution/get_execution.py index 263ce9c66..db0279b1f 100644 --- a/agents-api/agents_api/models/execution/get_execution.py +++ b/agents-api/agents_api/models/execution/get_execution.py @@ -13,6 +13,7 @@ rewrap_exceptions, wrap_in_class, ) +from .constants import OUTPUT_UNNEST_KEY ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -26,7 +27,16 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(Execution, one=True) +@wrap_in_class( + Execution, + one=True, + transform=lambda d: { + **d, + "output": d["output"][OUTPUT_UNNEST_KEY] + if isinstance(d["output"], dict) and OUTPUT_UNNEST_KEY in d["output"] + else d["output"], + }, +) @cozo_query @beartype def get_execution( diff --git a/agents-api/agents_api/models/execution/list_executions.py b/agents-api/agents_api/models/execution/list_executions.py index 09194cdbd..64add074f 100644 --- a/agents-api/agents_api/models/execution/list_executions.py +++ b/agents-api/agents_api/models/execution/list_executions.py @@ -15,6 +15,7 @@ verify_developer_owns_resource_query, wrap_in_class, ) +from .constants import OUTPUT_UNNEST_KEY ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -27,7 +28,15 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(Execution) +@wrap_in_class( + Execution, + transform=lambda d: { + **d, + "output": d["output"][OUTPUT_UNNEST_KEY] + if isinstance(d.get("output"), dict) and OUTPUT_UNNEST_KEY in d["output"] + else d.get("output"), + }, +) @cozo_query @beartype def list_executions( @@ -49,6 +58,7 @@ def list_executions( task_id, status, input, + output, session_id, metadata, created_at, @@ -59,6 +69,7 @@ def list_executions( execution_id: id, status, input, + output, session_id, metadata, created_at, diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index 513c44a16..5e841b9f2 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -31,6 +31,11 @@ QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), + AssertionError: lambda e: HTTPException( + status_code=429, + detail=str(e), + headers={"x-should-retry": "true"}, + ), } ) @wrap_in_class( diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index 4d0367ae4..35deab259 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -14,6 +14,7 @@ valid_previous_statuses as valid_previous_statuses_map, ) from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -22,6 +23,7 @@ verify_developer_owns_resource_query, wrap_in_class, ) +from .constants import OUTPUT_UNNEST_KEY ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -41,6 +43,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("update_execution") @beartype def update_execution( *, @@ -48,7 +51,7 @@ def update_execution( task_id: UUID, execution_id: UUID, data: UpdateExecutionRequest, - output: dict | None = None, + output: dict | Any | None = None, error: str | None = None, ) -> tuple[list[str], dict]: developer_id = str(developer_id) @@ -61,6 +64,9 @@ def update_execution( execution_data: dict = data.model_dump(exclude_none=True) + if output is not None and not isinstance(output, dict): + output: dict = {OUTPUT_UNNEST_KEY: output} + columns, values = cozo_process_mutate_data( { **execution_data, diff --git a/agents-api/agents_api/models/files/__init__.py b/agents-api/agents_api/models/files/__init__.py new file mode 100644 index 000000000..444c0a6eb --- /dev/null +++ b/agents-api/agents_api/models/files/__init__.py @@ -0,0 +1,3 @@ +from .create_file import create_file as create_file +from .delete_file import delete_file as delete_file +from .get_file import get_file as get_file diff --git a/agents-api/agents_api/models/files/create_file.py b/agents-api/agents_api/models/files/create_file.py new file mode 100644 index 000000000..224597180 --- /dev/null +++ b/agents-api/agents_api/models/files/create_file.py @@ -0,0 +1,121 @@ +""" +This module contains the functionality for creating a new user in the CozoDB database. +It defines a query for inserting user data into the 'users' relation. +""" + +import base64 +import hashlib +from typing import Any, TypeVar +from uuid import UUID, uuid4 + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import CreateFileRequest, File +from ...metrics.counters import increase_counter +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + + +@rewrap_exceptions( + { + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="Developer not found. Please ensure the provided auth token (which refers to your developer_id) is valid and the developer has the necessary permissions to create an agent.", + status_code=403, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), + } +) +@wrap_in_class( + File, + one=True, + transform=lambda d: { + **d, + "id": d["file_id"], + "content": "DUMMY: NEED TO FETCH CONTENT FROM BLOB STORAGE", + }, + _kind="inserted", +) +@cozo_query +@increase_counter("create_file") +@beartype +def create_file( + *, + developer_id: UUID, + file_id: UUID | None = None, + data: CreateFileRequest, +) -> tuple[list[str], dict]: + """ + Constructs and executes a datalog query to create a new file in the CozoDB database. + + Parameters: + user_id (UUID): The unique identifier for the user. + developer_id (UUID): The unique identifier for the developer creating the file. + """ + + file_id = file_id or uuid4() + file_data = data.model_dump(exclude={"content"}) + + content_bytes = base64.b64decode(data.content) + size = len(content_bytes) + hash = hashlib.sha256(content_bytes).hexdigest() + + create_query = """ + # Then create the file + ?[file_id, developer_id, name, description, mime_type, size, hash] <- [ + [to_uuid($file_id), to_uuid($developer_id), $name, $description, $mime_type, $size, $hash] + ] + + :insert files { + developer_id, + file_id => + name, + description, + mime_type, + size, + hash, + } + :returning + """ + + queries = [ + verify_developer_id_query(developer_id), + create_query, + ] + + return ( + queries, + { + "file_id": str(file_id), + "developer_id": str(developer_id), + "size": size, + "hash": hash, + **file_data, + }, + ) diff --git a/agents-api/agents_api/models/files/delete_file.py b/agents-api/agents_api/models/files/delete_file.py new file mode 100644 index 000000000..053402e2f --- /dev/null +++ b/agents-api/agents_api/models/files/delete_file.py @@ -0,0 +1,97 @@ +""" +This module contains the implementation of the delete_user_query function, which is responsible for deleting an user and its related default settings from the CozoDB database. +""" + +from typing import Any, TypeVar +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import ResourceDeletedResponse +from ...common.utils.datetime import utcnow +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + + +@rewrap_exceptions( + { + lambda e: isinstance(e, QueryException) + and "Developer does not exist" in str(e): lambda *_: HTTPException( + detail="The specified developer does not exist.", + status_code=403, + ), + lambda e: isinstance(e, QueryException) + and "Developer does not own resource" + in e.resp["display"]: lambda *_: HTTPException( + detail="The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct.", + status_code=404, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), + } +) +@wrap_in_class( + ResourceDeletedResponse, + one=True, + transform=lambda d: { + "id": UUID(d.pop("file_id")), + "deleted_at": utcnow(), + "jobs": [], + }, + _kind="deleted", +) +@cozo_query +@beartype +def delete_file(*, developer_id: UUID, file_id: UUID) -> tuple[list[str], dict]: + """ + Constructs and returns a datalog query for deleting an file from the database. + + Parameters: + developer_id (UUID): The UUID of the developer owning the file. + file_id (UUID): The UUID of the file to be deleted. + client (CozoClient, optional): An instance of the CozoClient to execute the query. + + Returns: + ResourceDeletedResponse: The response indicating the deletion of the user. + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query(developer_id, "files", file_id=file_id), + """ + ?[file_id, developer_id] <- [[$file_id, $developer_id]] + + :delete files { + developer_id, + file_id + } + :returning + """, + ] + + return (queries, {"file_id": str(file_id), "developer_id": str(developer_id)}) diff --git a/agents-api/agents_api/models/files/get_file.py b/agents-api/agents_api/models/files/get_file.py new file mode 100644 index 000000000..f3b85c2f7 --- /dev/null +++ b/agents-api/agents_api/models/files/get_file.py @@ -0,0 +1,116 @@ +from typing import Any, TypeVar +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import File +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + + +@rewrap_exceptions( + { + lambda e: isinstance(e, QueryException) + and "Developer does not exist" in str(e): lambda *_: HTTPException( + detail="The specified developer does not exist.", + status_code=403, + ), + lambda e: isinstance(e, QueryException) + and "Developer does not own resource" + in e.resp["display"]: lambda *_: HTTPException( + detail="The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct.", + status_code=404, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), + } +) +@wrap_in_class( + File, + one=True, + transform=lambda d: { + **d, + "content": "DUMMY: NEED TO FETCH CONTENT FROM BLOB STORAGE", + }, +) +@cozo_query +@beartype +def get_file( + *, + developer_id: UUID, + file_id: UUID, +) -> tuple[list[str], dict]: + """ + Retrieves a file by their unique identifier. + + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the file. + file_id (UUID): The unique identifier of the file to retrieve. + + Returns: + File: The retrieved file. + """ + + # Convert UUIDs to strings for query compatibility. + file_id = str(file_id) + developer_id = str(developer_id) + + get_query = """ + input[developer_id, file_id] <- [[to_uuid($developer_id), to_uuid($file_id)]] + + ?[ + id, + name, + description, + mime_type, + size, + hash, + created_at, + ] := input[developer_id, id], + *files { + file_id: id, + developer_id, + name, + description, + mime_type, + size, + hash, + created_at, + } + + :limit 1 + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query(developer_id, "files", file_id=file_id), + get_query, + ] + + return (queries, {"developer_id": developer_id, "file_id": file_id}) diff --git a/agents-api/agents_api/models/session/__init__.py b/agents-api/agents_api/models/session/__init__.py index bc5f7fbb4..bf80c9f4b 100644 --- a/agents-api/agents_api/models/session/__init__.py +++ b/agents-api/agents_api/models/session/__init__.py @@ -11,6 +11,7 @@ # ruff: noqa: F401, F403, F405 +from .count_sessions import count_sessions from .create_or_update_session import create_or_update_session from .create_session import create_session from .delete_session import delete_session diff --git a/agents-api/agents_api/models/session/count_sessions.py b/agents-api/agents_api/models/session/count_sessions.py new file mode 100644 index 000000000..3599cc2fb --- /dev/null +++ b/agents-api/agents_api/models/session/count_sessions.py @@ -0,0 +1,64 @@ +"""This module contains functions for querying session data from the 'cozodb' database.""" + +from typing import Any, TypeVar +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class(dict, one=True) +@cozo_query +@beartype +def count_sessions( + *, + developer_id: UUID, +) -> tuple[list[str], dict]: + """ + Counts sessions from the 'cozodb' database. + + Parameters: + developer_id (UUID): The developer's ID to filter sessions by. + """ + + count_query = """ + input[developer_id] <- [[ + to_uuid($developer_id), + ]] + + counter[count(id)] := + input[developer_id], + *sessions{ + developer_id, + session_id: id, + } + + ?[count] := counter[count] + """ + + queries = [ + verify_developer_id_query(developer_id), + count_query, + ] + + return (queries, {"developer_id": str(developer_id)}) diff --git a/agents-api/agents_api/models/session/create_or_update_session.py b/agents-api/agents_api/models/session/create_or_update_session.py index 6c81b2563..e34a63ca5 100644 --- a/agents-api/agents_api/models/session/create_or_update_session.py +++ b/agents-api/agents_api/models/session/create_or_update_session.py @@ -11,6 +11,7 @@ ResourceUpdatedResponse, ) from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -43,6 +44,7 @@ }, ) @cozo_query +@increase_counter("create_or_update_session") @beartype def create_or_update_session( *, @@ -51,7 +53,7 @@ def create_or_update_session( data: CreateOrUpdateSessionRequest, ) -> tuple[list[str], dict]: data.metadata = data.metadata or {} - session_data = data.model_dump() + session_data = data.model_dump(exclude={"auto_run_tools", "disable_cache"}) user = session_data.pop("user") agent = session_data.pop("agent") diff --git a/agents-api/agents_api/models/session/create_session.py b/agents-api/agents_api/models/session/create_session.py index 32a74533a..ce804399d 100644 --- a/agents-api/agents_api/models/session/create_session.py +++ b/agents-api/agents_api/models/session/create_session.py @@ -12,6 +12,7 @@ from pydantic import ValidationError from ...autogen.openapi_model import CreateSessionRequest, Session +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -44,6 +45,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_session") @beartype def create_session( *, @@ -58,7 +60,7 @@ def create_session( session_id = session_id or uuid4() data.metadata = data.metadata or {} - session_data = data.model_dump() + session_data = data.model_dump(exclude={"auto_run_tools", "disable_cache"}) user = session_data.pop("user") agent = session_data.pop("agent") diff --git a/agents-api/agents_api/models/session/update_session.py b/agents-api/agents_api/models/session/update_session.py index 3d3a685d1..cc8b61f16 100644 --- a/agents-api/agents_api/models/session/update_session.py +++ b/agents-api/agents_api/models/session/update_session.py @@ -8,6 +8,7 @@ from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateSessionRequest from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -51,6 +52,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("update_session") @beartype def update_session( *, @@ -58,6 +60,18 @@ def update_session( developer_id: UUID, data: UpdateSessionRequest, ) -> tuple[list[str], dict]: + """ + Updates a session with the provided data. + + Parameters: + session_id (UUID): The unique identifier of the session to update. + developer_id (UUID): The unique identifier of the developer associated with the session. + data (UpdateSessionRequest): The data to update the session with. + + Returns: + ResourceUpdatedResponse: The updated session. + """ + update_data = data.model_dump(exclude_unset=True) session_update_cols, session_update_vals = cozo_process_mutate_data( diff --git a/agents-api/agents_api/models/task/create_or_update_task.py b/agents-api/agents_api/models/task/create_or_update_task.py index d787d78b5..1f615a3ad 100644 --- a/agents-api/agents_api/models/task/create_or_update_task.py +++ b/agents-api/agents_api/models/task/create_or_update_task.py @@ -18,6 +18,7 @@ from ...common.protocol.tasks import task_to_spec from ...common.utils.cozo import cozo_process_mutate_data from ...common.utils.datetime import utcnow +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -49,6 +50,7 @@ }, ) @cozo_query +@increase_counter("create_or_update_task") @beartype def create_or_update_task( *, @@ -64,9 +66,7 @@ def create_or_update_task( data.metadata = data.metadata or {} data.input_schema = data.input_schema or {} - task_data = task_to_spec(data).model_dump( - exclude_none=True, exclude_unset=True, mode="json" - ) + task_data = task_to_spec(data).model_dump(exclude_none=True, mode="json") task_data.pop("task_id", None) task_data["created_at"] = utcnow().timestamp() diff --git a/agents-api/agents_api/models/task/create_task.py b/agents-api/agents_api/models/task/create_task.py index 9affe0ead..ab68a5b0c 100644 --- a/agents-api/agents_api/models/task/create_task.py +++ b/agents-api/agents_api/models/task/create_task.py @@ -13,9 +13,11 @@ from ...autogen.openapi_model import ( CreateTaskRequest, + ResourceCreatedResponse, ) -from ...common.protocol.tasks import spec_to_task, task_to_spec +from ...common.protocol.tasks import task_to_spec from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -36,8 +38,18 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(spec_to_task, one=True, _kind="inserted") +@wrap_in_class( + ResourceCreatedResponse, + one=True, + transform=lambda d: { + "id": d["task_id"], + "jobs": [], + "created_at": d["created_at"], + **d, + }, +) @cozo_query +@increase_counter("create_task") @beartype def create_task( *, @@ -46,6 +58,19 @@ def create_task( task_id: UUID | None = None, data: CreateTaskRequest, ) -> tuple[list[str], dict]: + """ + Creates a new task. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the task. + agent_id (UUID): The unique identifier of the agent associated with the task. + task_id (UUID | None): The unique identifier of the task. If not provided, a new UUID will be generated. + data (CreateTaskRequest): The data to create the task with. + + Returns: + ResourceCreatedResponse: The created task. + """ + data.metadata = data.metadata or {} data.input_schema = data.input_schema or {} @@ -55,7 +80,7 @@ def create_task( # Prepares the update data by filtering out None values and adding user_id and developer_id. columns, values = cozo_process_mutate_data( { - **task_spec.model_dump(exclude_none=True, exclude_unset=True, mode="json"), + **task_spec.model_dump(exclude_none=True, mode="json"), "task_id": str(task_id), "agent_id": str(agent_id), } diff --git a/agents-api/agents_api/models/task/delete_task.py b/agents-api/agents_api/models/task/delete_task.py index 60d6f2756..10c377a25 100644 --- a/agents-api/agents_api/models/task/delete_task.py +++ b/agents-api/agents_api/models/task/delete_task.py @@ -47,6 +47,18 @@ def delete_task( agent_id: UUID, task_id: UUID, ) -> tuple[list[str], dict]: + """ + Deletes a task. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the task. + agent_id (UUID): The unique identifier of the agent associated with the task. + task_id (UUID): The unique identifier of the task to delete. + + Returns: + ResourceDeletedResponse: The deleted task. + """ + delete_query = """ input[agent_id, task_id] <- [[ to_uuid($agent_id), diff --git a/agents-api/agents_api/models/task/get_task.py b/agents-api/agents_api/models/task/get_task.py index 4e5b2fb97..460fdc38b 100644 --- a/agents-api/agents_api/models/task/get_task.py +++ b/agents-api/agents_api/models/task/get_task.py @@ -6,6 +6,7 @@ from pycozo.client import QueryException from pydantic import ValidationError +from ...common.protocol.tasks import spec_to_task from ..utils import ( cozo_query, partialclass, @@ -14,7 +15,6 @@ verify_developer_owns_resource_query, wrap_in_class, ) -from .create_task import spec_to_task ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -35,6 +35,17 @@ def get_task( developer_id: UUID, task_id: UUID, ) -> tuple[list[str], dict]: + """ + Retrieves a task by its unique identifier. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the task. + task_id (UUID): The unique identifier of the task to retrieve. + + Returns: + Task | CreateTaskRequest: The retrieved task. + """ + get_query = """ input[task_id] <- [[to_uuid($task_id)]] diff --git a/agents-api/agents_api/models/task/list_tasks.py b/agents-api/agents_api/models/task/list_tasks.py index a443ce9e2..d873e817e 100644 --- a/agents-api/agents_api/models/task/list_tasks.py +++ b/agents-api/agents_api/models/task/list_tasks.py @@ -6,6 +6,7 @@ from pycozo.client import QueryException from pydantic import ValidationError +from ...common.protocol.tasks import spec_to_task from ..utils import ( cozo_query, partialclass, @@ -14,7 +15,6 @@ verify_developer_owns_resource_query, wrap_in_class, ) -from .create_task import spec_to_task ModelT = TypeVar("ModelT", bound=Any) T = TypeVar("T") @@ -39,6 +39,21 @@ def list_tasks( sort_by: Literal["created_at", "updated_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", ) -> tuple[list[str], dict]: + """ + Lists tasks for a given agent. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the tasks. + agent_id (UUID): The unique identifier of the agent associated with the tasks. + limit (int): The maximum number of tasks to return. + offset (int): The number of tasks to skip before returning the results. + sort_by (Literal["created_at", "updated_at"]): The field to sort the tasks by. + direction (Literal["asc", "desc"]): The direction to sort the tasks in. + + Returns: + Task[] | CreateTaskRequest[]: The list of tasks. + """ + sort = f"{'-' if direction == 'desc' else ''}{sort_by}" list_query = f""" diff --git a/agents-api/agents_api/models/task/patch_task.py b/agents-api/agents_api/models/task/patch_task.py index 1837064c7..178b9daa3 100644 --- a/agents-api/agents_api/models/task/patch_task.py +++ b/agents-api/agents_api/models/task/patch_task.py @@ -14,6 +14,7 @@ from ...autogen.openapi_model import PatchTaskRequest, ResourceUpdatedResponse, TaskSpec from ...common.protocol.tasks import task_to_spec from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -46,6 +47,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("patch_task") @beartype def patch_task( *, diff --git a/agents-api/agents_api/models/task/update_task.py b/agents-api/agents_api/models/task/update_task.py index 9cfb04357..cd98d85d5 100644 --- a/agents-api/agents_api/models/task/update_task.py +++ b/agents-api/agents_api/models/task/update_task.py @@ -14,6 +14,7 @@ from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateTaskRequest from ...common.protocol.tasks import task_to_spec from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -45,6 +46,7 @@ }, ) @cozo_query +@increase_counter("update_task") @beartype def update_task( *, @@ -53,6 +55,19 @@ def update_task( task_id: UUID, data: UpdateTaskRequest, ) -> tuple[list[str], dict]: + """ + Updates a task. + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the task. + agent_id (UUID): The unique identifier of the agent associated with the task. + task_id (UUID): The unique identifier of the task to update. + data (UpdateTaskRequest): The data to update the task with. + + Returns: + ResourceUpdatedResponse: The updated task. + """ + developer_id = str(developer_id) agent_id = str(agent_id) task_id = str(task_id) diff --git a/agents-api/agents_api/models/tools/create_tools.py b/agents-api/agents_api/models/tools/create_tools.py index b98a751d0..9b2be387a 100644 --- a/agents-api/agents_api/models/tools/create_tools.py +++ b/agents-api/agents_api/models/tools/create_tools.py @@ -9,6 +9,7 @@ from pydantic import ValidationError from ...autogen.openapi_model import CreateToolRequest, Tool +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -27,6 +28,7 @@ QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), + AssertionError: partialclass(HTTPException, status_code=400), } ) @wrap_in_class( @@ -39,6 +41,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_tools") @beartype def create_tools( *, @@ -58,13 +61,19 @@ def create_tools( list[Tool] """ + assert all( + getattr(tool, tool.type) is not None + for tool in data + if hasattr(tool, tool.type) + ), "Tool spec must be passed" + tools_data = [ [ str(agent_id), str(uuid4()), tool.type, tool.name, - getattr(tool, tool.type).dict(), + getattr(tool, tool.type) and getattr(tool, tool.type).model_dump(), tool.description if hasattr(tool, "description") else None, ] for tool in data diff --git a/agents-api/agents_api/models/tools/patch_tool.py b/agents-api/agents_api/models/tools/patch_tool.py index 0d8304d7d..bc49b8121 100644 --- a/agents-api/agents_api/models/tools/patch_tool.py +++ b/agents-api/agents_api/models/tools/patch_tool.py @@ -8,6 +8,7 @@ from ...autogen.openapi_model import PatchToolRequest, ResourceUpdatedResponse from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -35,6 +36,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("patch_tool") @beartype def patch_tool( *, developer_id: UUID, agent_id: UUID, tool_id: UUID, data: PatchToolRequest diff --git a/agents-api/agents_api/models/tools/update_tool.py b/agents-api/agents_api/models/tools/update_tool.py index d1676e984..ef700a5f6 100644 --- a/agents-api/agents_api/models/tools/update_tool.py +++ b/agents-api/agents_api/models/tools/update_tool.py @@ -11,6 +11,7 @@ UpdateToolRequest, ) from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -38,6 +39,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("update_tool") @beartype def update_tool( *, diff --git a/agents-api/agents_api/models/user/create_or_update_user.py b/agents-api/agents_api/models/user/create_or_update_user.py index d295d1d8a..3e9b1f3a6 100644 --- a/agents-api/agents_api/models/user/create_or_update_user.py +++ b/agents-api/agents_api/models/user/create_or_update_user.py @@ -12,6 +12,7 @@ from pydantic import ValidationError from ...autogen.openapi_model import CreateOrUpdateUserRequest, User +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -26,13 +27,26 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), } ) @wrap_in_class(User, one=True, transform=lambda d: {"id": UUID(d.pop("user_id")), **d}) @cozo_query +@increase_counter("create_or_update_user") @beartype def create_or_update_user( *, diff --git a/agents-api/agents_api/models/user/create_user.py b/agents-api/agents_api/models/user/create_user.py index 9dd036c57..ba96bd2b5 100644 --- a/agents-api/agents_api/models/user/create_user.py +++ b/agents-api/agents_api/models/user/create_user.py @@ -12,6 +12,7 @@ from pydantic import ValidationError from ...autogen.openapi_model import CreateUserRequest, User +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -29,11 +30,24 @@ lambda e: isinstance(e, QueryException) and "asserted to return some results, but returned none" in str(e): lambda *_: HTTPException( - detail="developer not found", status_code=403 + detail="Developer not found. Please ensure the provided auth token (which refers to your developer_id) is valid and the developer has the necessary permissions to create an agent.", + status_code=403, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", ), - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), } ) @wrap_in_class( @@ -43,6 +57,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("create_user") @beartype def create_user( *, diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py index 0532f5cfa..7f08316be 100644 --- a/agents-api/agents_api/models/user/delete_user.py +++ b/agents-api/agents_api/models/user/delete_user.py @@ -27,9 +27,32 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + lambda e: isinstance(e, QueryException) + and "Developer does not exist" in str(e): lambda *_: HTTPException( + detail="The specified developer does not exist.", + status_code=403, + ), + lambda e: isinstance(e, QueryException) + and "Developer does not own resource" + in e.resp["display"]: lambda *_: HTTPException( + detail="The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct.", + status_code=404, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), } ) @wrap_in_class( diff --git a/agents-api/agents_api/models/user/get_user.py b/agents-api/agents_api/models/user/get_user.py index 2b4f59c83..69b3da883 100644 --- a/agents-api/agents_api/models/user/get_user.py +++ b/agents-api/agents_api/models/user/get_user.py @@ -23,17 +23,31 @@ @rewrap_exceptions( { lambda e: isinstance(e, QueryException) - and "Developer not found" in str(e): lambda *_: HTTPException( - detail="developer does not exist", status_code=403 + and "Developer does not exist" in str(e): lambda *_: HTTPException( + detail="The specified developer does not exist.", + status_code=403, ), lambda e: isinstance(e, QueryException) and "Developer does not own resource" in e.resp["display"]: lambda *_: HTTPException( - detail="developer doesnt own resource", status_code=404 + detail="The specified developer does not own the requested resource. Please verify the ownership or check if the developer ID is correct.", + status_code=404, + ), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", ), - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), } ) @wrap_in_class(User, one=True) @@ -44,6 +58,18 @@ def get_user( developer_id: UUID, user_id: UUID, ) -> tuple[list[str], dict]: + """ + Retrieves a user by their unique identifier. + + + Parameters: + developer_id (UUID): The unique identifier of the developer associated with the user. + user_id (UUID): The unique identifier of the user to retrieve. + + Returns: + User: The retrieved user. + """ + # Convert UUIDs to strings for query compatibility. user_id = str(user_id) developer_id = str(developer_id) diff --git a/agents-api/agents_api/models/user/list_users.py b/agents-api/agents_api/models/user/list_users.py index 2a810b8e0..f1e06adf4 100644 --- a/agents-api/agents_api/models/user/list_users.py +++ b/agents-api/agents_api/models/user/list_users.py @@ -22,9 +22,21 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), } ) @wrap_in_class(User) @@ -46,6 +58,8 @@ def list_users( developer_id (UUID): The unique identifier of the developer. limit (int): The maximum number of users to return. Defaults to 100. offset (int): The number of users to skip before starting to collect the result set. Defaults to 0. + sort_by (Literal["created_at", "updated_at"]): The field to sort the users by. Defaults to "created_at". + direction (Literal["asc", "desc"]): The direction to sort the users in. Defaults to "desc". metadata_filter (dict[str, Any]): A dictionary representing filters to apply on user metadata. Returns: diff --git a/agents-api/agents_api/models/user/patch_user.py b/agents-api/agents_api/models/user/patch_user.py index 265241d47..e091edc63 100644 --- a/agents-api/agents_api/models/user/patch_user.py +++ b/agents-api/agents_api/models/user/patch_user.py @@ -11,6 +11,7 @@ from ...autogen.openapi_model import PatchUserRequest, ResourceUpdatedResponse from ...common.utils.cozo import cozo_process_mutate_data from ...common.utils.datetime import utcnow +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -26,9 +27,21 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), } ) @wrap_in_class( @@ -38,6 +51,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("patch_user") @beartype def patch_user( *, diff --git a/agents-api/agents_api/models/user/update_user.py b/agents-api/agents_api/models/user/update_user.py index fd8e7e2c8..68e6e6c25 100644 --- a/agents-api/agents_api/models/user/update_user.py +++ b/agents-api/agents_api/models/user/update_user.py @@ -8,6 +8,7 @@ from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateUserRequest from ...common.utils.cozo import cozo_process_mutate_data +from ...metrics.counters import increase_counter from ..utils import ( cozo_query, partialclass, @@ -23,9 +24,21 @@ @rewrap_exceptions( { - QueryException: partialclass(HTTPException, status_code=400), - ValidationError: partialclass(HTTPException, status_code=400), - TypeError: partialclass(HTTPException, status_code=400), + QueryException: partialclass( + HTTPException, + status_code=400, + detail="A database query failed to return the expected results. This might occur if the requested resource doesn't exist or your query parameters are incorrect.", + ), + ValidationError: partialclass( + HTTPException, + status_code=400, + detail="Input validation failed. Please check the provided data for missing or incorrect fields, and ensure it matches the required format.", + ), + TypeError: partialclass( + HTTPException, + status_code=400, + detail="A type mismatch occurred. This likely means the data provided is of an incorrect type (e.g., string instead of integer). Please review the input and try again.", + ), } ) @wrap_in_class( @@ -35,6 +48,7 @@ _kind="inserted", ) @cozo_query +@increase_counter("update_user") @beartype def update_user( *, developer_id: UUID, user_id: UUID, data: UpdateUserRequest diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index f63646e1c..32fd5b8bd 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -1,10 +1,12 @@ import inspect import re +import time from functools import partialmethod, wraps -from typing import Any, Callable, ParamSpec, Type, TypeVar +from typing import Any, Awaitable, Callable, ParamSpec, Type, TypeVar from uuid import UUID import pandas as pd +from fastapi import HTTPException from pydantic import BaseModel from ..common.utils.cozo import uuid_int_list_to_uuid4 @@ -186,6 +188,8 @@ def make_cozo_json_query(fields): def cozo_query( func: Callable[P, tuple[str | list[str | None], dict]] | None = None, debug: bool | None = None, + only_on_error: bool = False, + timeit: bool = False, ): def cozo_query_dec(func: Callable[P, tuple[str | list[Any], dict]]): """ @@ -198,6 +202,21 @@ def cozo_query_dec(func: Callable[P, tuple[str | list[Any], dict]]): from pprint import pprint + from tenacity import ( + retry, + retry_if_exception, + stop_after_attempt, + wait_exponential, + ) + + def is_resource_busy(e: Exception) -> bool: + return isinstance(e, HTTPException) and e.status_code == 429 + + @retry( + stop=stop_after_attempt(4), + wait=wait_exponential(multiplier=1, min=4, max=10), + retry=retry_if_exception(is_resource_busy), + ) @wraps(func) def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: queries, variables = func(*args, **kwargs) @@ -209,8 +228,8 @@ def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: query = "}\n\n{\n".join(queries) query = f"{{ {query} }}" - debug and print(query) - debug and pprint( + not only_on_error and debug and print(query) + not only_on_error and debug and pprint( dict( variables=variables, ) @@ -221,16 +240,144 @@ def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: try: client = client or cozo.get_cozo_client() + + start = timeit and time.perf_counter() result = client.run(query, variables) + end = timeit and time.perf_counter() + + timeit and print(f"Cozo query time: {end - start:.2f} seconds") + + except Exception as e: + if only_on_error and debug: + print(query) + pprint(variables) + + debug and print(repr(e)) + + if "busy" in (str(e) + str(getattr(e, "resp", e))).lower(): + raise HTTPException( + status_code=429, detail="Resource busy. Please try again later." + ) from e + + raise + + # Need to fix the UUIDs in the result + result = result.map(fix_uuid_if_present) + + not only_on_error and debug and pprint( + dict( + result=result.to_dict(orient="records"), + ) + ) + + return result + + # Set the wrapped function as an attribute of the wrapper, + # forwards the __wrapped__ attribute if it exists. + setattr(wrapper, "__wrapped__", getattr(func, "__wrapped__", func)) + + return wrapper + + if func is not None and callable(func): + return cozo_query_dec(func) + + return cozo_query_dec + + +def cozo_query_async( + func: Callable[ + P, + tuple[str | list[str | None], dict] + | Awaitable[tuple[str | list[str | None], dict]], + ] + | None = None, + debug: bool | None = None, + only_on_error: bool = False, + timeit: bool = False, +): + def cozo_query_dec( + func: Callable[ + P, tuple[str | list[Any], dict] | Awaitable[tuple[str | list[Any], dict]] + ], + ): + """ + Decorator that wraps a function that takes arbitrary arguments, and + returns a (query string, variables) tuple. + + The wrapped function should additionally take a client keyword argument + and then run the query using the client, returning a DataFrame. + """ + + from pprint import pprint + + from tenacity import ( + retry, + retry_if_exception, + stop_after_attempt, + wait_exponential, + ) + + def is_resource_busy(e: Exception) -> bool: + return isinstance(e, HTTPException) and e.status_code == 429 + + @retry( + stop=stop_after_attempt(4), + wait=wait_exponential(multiplier=1, min=4, max=10), + retry=retry_if_exception(is_resource_busy), + ) + @wraps(func) + async def wrapper( + *args: P.args, client=None, **kwargs: P.kwargs + ) -> pd.DataFrame: + if inspect.iscoroutinefunction(func): + queries, variables = await func(*args, **kwargs) + else: + queries, variables = func(*args, **kwargs) + + if isinstance(queries, str): + query = queries + else: + queries = [str(query) for query in queries if query] + query = "}\n\n{\n".join(queries) + query = f"{{ {query} }}" + + not only_on_error and debug and print(query) + not only_on_error and debug and pprint( + dict( + variables=variables, + ) + ) + + # Run the query + from ..clients import cozo + + try: + client = client or cozo.get_async_cozo_client() + + start = timeit and time.perf_counter() + result = await client.run(query, variables) + end = timeit and time.perf_counter() + + timeit and print(f"Cozo query time: {end - start:.2f} seconds") except Exception as e: - debug and print(repr(getattr(e, "__cause__", None) or e)) + if only_on_error and debug: + print(query) + pprint(variables) + + debug and print(repr(e)) + + if "busy" in (str(e) + str(getattr(e, "resp", e))).lower(): + raise HTTPException( + status_code=429, detail="Resource busy. Please try again later." + ) from e + raise # Need to fix the UUIDs in the result result = result.map(fix_uuid_if_present) - debug and pprint( + not only_on_error and debug and pprint( dict( result=result.to_dict(orient="records"), ) @@ -256,33 +403,41 @@ def wrap_in_class( transform: Callable[[dict], dict] | None = None, _kind: str | None = None, ): - def decorator(func: Callable[P, pd.DataFrame]): - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> ModelT | list[ModelT]: - df = func(*args, **kwargs) + def _return_data(df: pd.DataFrame): + # Convert df to list of dicts + if _kind: + df = df[df["_kind"] == _kind] - # Convert df to list of dicts - if _kind: - df = df[df["_kind"] == _kind] + data = df.to_dict(orient="records") - data = df.to_dict(orient="records") + nonlocal transform + transform = transform or (lambda x: x) - nonlocal transform - transform = transform or (lambda x: x) + if one: + assert len(data) >= 1, "Expected one result, got none" + obj: ModelT = cls(**transform(data[0])) + return obj - if one: - assert len(data) >= 1, "Expected one result, got none" - obj: ModelT = cls(**transform(data[0])) - return obj + objs: list[ModelT] = [cls(**item) for item in map(transform, data)] + return objs - objs: list[ModelT] = [cls(**item) for item in map(transform, data)] - return objs + def decorator(func: Callable[P, pd.DataFrame | Awaitable[pd.DataFrame]]): + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> ModelT | list[ModelT]: + return _return_data(func(*args, **kwargs)) + + @wraps(func) + async def async_wrapper( + *args: P.args, **kwargs: P.kwargs + ) -> ModelT | list[ModelT]: + return _return_data(await func(*args, **kwargs)) # Set the wrapped function as an attribute of the wrapper, # forwards the __wrapped__ attribute if it exists. setattr(wrapper, "__wrapped__", getattr(func, "__wrapped__", func)) + setattr(async_wrapper, "__wrapped__", getattr(func, "__wrapped__", func)) - return wrapper + return async_wrapper if inspect.iscoroutinefunction(func) else wrapper return decorator @@ -294,33 +449,42 @@ def rewrap_exceptions( ], /, ): - def decorator(func: Callable[P, T]): - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: - nonlocal mapping + def _check_error(error): + nonlocal mapping - try: - result: T = func(*args, **kwargs) + for check, transform in mapping.items(): + should_catch = ( + isinstance(error, check) if isinstance(check, type) else check(error) + ) - except BaseException as error: - for check, transform in mapping.items(): - should_catch = ( - isinstance(error, check) - if isinstance(check, type) - else check(error) - ) + if should_catch: + new_error = ( + transform(str(error)) + if isinstance(transform, type) + else transform(error) + ) - if should_catch: - new_error = ( - transform(str(error)) - if isinstance(transform, type) - else transform(error) - ) + setattr(new_error, "__cause__", error) - setattr(new_error, "__cause__", error) + raise new_error from error - raise new_error from error + def decorator(func: Callable[P, T | Awaitable[T]]): + @wraps(func) + async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + try: + result: T = await func(*args, **kwargs) + except BaseException as error: + _check_error(error) + raise + return result + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + try: + result: T = func(*args, **kwargs) + except BaseException as error: + _check_error(error) raise return result @@ -328,7 +492,8 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: # Set the wrapped function as an attribute of the wrapper, # forwards the __wrapped__ attribute if it exists. setattr(wrapper, "__wrapped__", getattr(func, "__wrapped__", func)) + setattr(async_wrapper, "__wrapped__", getattr(func, "__wrapped__", func)) - return wrapper + return async_wrapper if inspect.iscoroutinefunction(func) else wrapper return decorator diff --git a/agents-api/agents_api/routers/__init__.py b/agents-api/agents_api/routers/__init__.py index e3b80498e..4e2d7b881 100644 --- a/agents-api/agents_api/routers/__init__.py +++ b/agents-api/agents_api/routers/__init__.py @@ -6,6 +6,8 @@ - `sessions`: Manages routing for session-related operations. This includes creating, updating, and deleting sessions, as well as handling chat functionalities and history within sessions. It is essential for managing interactive sessions between agents and users. - `users`: Responsible for routing user-related operations. This encompasses user creation, update, deletion, and managing user documents. It ensures that user data can be properly managed and accessed as needed. - `jobs`: Deals with routing for job status inquiries. This allows users to check the status of asynchronous jobs, providing insights into the progress and outcomes of long-running operations. +- `files`: Deals with routing for file operations. This includes creating, reading, updating, and deleting files. It provides endpoints for managing files, including uploading, downloading, and deleting file content. +- `docs`: Deals with routing for documentation operations. This includes creating, reading, updating, and deleting documentation. It provides endpoints for managing documentation, including uploading, downloading, and deleting documentation content. Each sub-module defines its own set of API endpoints and is responsible for handling requests and responses related to its domain, ensuring a modular and organized approach to API development. """ @@ -17,6 +19,8 @@ from .agents import router as agents_router from .docs import router as docs_router +from .files import router as files_router +from .internal import router as internal_router from .jobs import router as jobs_router from .sessions import router as sessions_router from .tasks import router as tasks_router diff --git a/agents-api/agents_api/routers/agents/list_agents.py b/agents-api/agents_api/routers/agents/list_agents.py index ef9bb09db..b96bec089 100644 --- a/agents-api/agents_api/routers/agents/list_agents.py +++ b/agents-api/agents_api/routers/agents/list_agents.py @@ -5,7 +5,7 @@ from ...autogen.openapi_model import Agent, ListResponse from ...dependencies.developer_id import get_developer_id -from ...dependencies.query_filter import create_filter_extractor +from ...dependencies.query_filter import MetadataFilter, create_filter_extractor from ...models.agent.list_agents import list_agents as list_agents_query from .router import router @@ -17,7 +17,7 @@ async def list_agents( # Example: # > ?metadata_filter.name=John&metadata_filter.age=30 metadata_filter: Annotated[ - dict, Depends(create_filter_extractor("metadata_filter")) + MetadataFilter, Depends(create_filter_extractor("metadata_filter")) ], limit: int = 100, offset: int = 0, @@ -30,7 +30,7 @@ async def list_agents( offset=offset, sort_by=sort_by, direction=direction, - metadata_filter=metadata_filter or {}, + metadata_filter=metadata_filter.model_dump(mode="json") or {}, ) return ListResponse[Agent](items=agents) diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index 16754dbe3..1aaf664c1 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -6,7 +6,7 @@ from temporalio.client import Client as TemporalClient from ...activities.types import EmbedDocsPayload -from ...autogen.openapi_model import CreateDocRequest, ResourceCreatedResponse +from ...autogen.openapi_model import CreateDocRequest, Doc, ResourceCreatedResponse from ...clients import temporal from ...common.retry_policies import DEFAULT_RETRY_POLICY from ...dependencies.developer_id import get_developer_id @@ -21,6 +21,7 @@ async def run_embed_docs_task( doc_id: UUID, title: str, content: list[str], + embed_instruction: str | None = None, job_id: UUID, background_tasks: BackgroundTasks, client: TemporalClient | None = None, @@ -34,7 +35,7 @@ async def run_embed_docs_task( doc_id=doc_id, content=content, title=title, - embed_instruction=None, + embed_instruction=embed_instruction, ) handle = await client.start_workflow( @@ -60,7 +61,20 @@ async def create_user_doc( x_developer_id: Annotated[UUID, Depends(get_developer_id)], background_tasks: BackgroundTasks, ) -> ResourceCreatedResponse: - doc = create_doc_query( + """ + Creates a new document for a user. + + Parameters: + user_id (UUID): The unique identifier of the user associated with the document. + data (CreateDocRequest): The data to create the document with. + x_developer_id (UUID): The unique identifier of the developer associated with the document. + background_tasks (BackgroundTasks): The background tasks to run. + + Returns: + ResourceCreatedResponse: The created document. + """ + + doc: Doc = create_doc_query( developer_id=x_developer_id, owner_type="user", owner_id=user_id, @@ -74,6 +88,7 @@ async def create_user_doc( doc_id=doc.id, title=doc.title, content=doc.content, + embed_instruction=data.embed_instruction, job_id=embed_job_id, background_tasks=background_tasks, ) @@ -90,7 +105,7 @@ async def create_agent_doc( x_developer_id: Annotated[UUID, Depends(get_developer_id)], background_tasks: BackgroundTasks, ) -> ResourceCreatedResponse: - doc = create_doc_query( + doc: Doc = create_doc_query( developer_id=x_developer_id, owner_type="agent", owner_id=agent_id, @@ -104,6 +119,7 @@ async def create_agent_doc( doc_id=doc.id, title=doc.title, content=doc.content, + embed_instruction=data.embed_instruction, job_id=embed_job_id, background_tasks=background_tasks, ) diff --git a/agents-api/agents_api/routers/docs/embed.py b/agents-api/agents_api/routers/docs/embed.py index 3e880fa74..1c13a1297 100644 --- a/agents-api/agents_api/routers/docs/embed.py +++ b/agents-api/agents_api/routers/docs/embed.py @@ -4,8 +4,9 @@ from fastapi import Depends from ...autogen.openapi_model import ( - EmbedQueryRequest, EmbedQueryResponse, + MultipleEmbedQueryRequest, + SingleEmbedQueryRequest, ) from ...clients import litellm from ...dependencies.developer_id import get_developer_id @@ -15,13 +16,14 @@ @router.post("/embed", tags=["docs"]) async def embed( x_developer_id: Annotated[UUID, Depends(get_developer_id)], - data: EmbedQueryRequest, + data: SingleEmbedQueryRequest | MultipleEmbedQueryRequest, ) -> EmbedQueryResponse: text_to_embed: str | list[str] = data.text text_to_embed: list[str] = ( [text_to_embed] if isinstance(text_to_embed, str) else text_to_embed ) - - vectors = await litellm.aembedding(inputs=text_to_embed) + vectors = await litellm.aembedding( + inputs=[data.embed_instruction + text for text in text_to_embed] + ) return EmbedQueryResponse(vectors=vectors) diff --git a/agents-api/agents_api/routers/docs/list_docs.py b/agents-api/agents_api/routers/docs/list_docs.py index a4701646d..2f663a324 100644 --- a/agents-api/agents_api/routers/docs/list_docs.py +++ b/agents-api/agents_api/routers/docs/list_docs.py @@ -5,7 +5,7 @@ from ...autogen.openapi_model import Doc, ListResponse from ...dependencies.developer_id import get_developer_id -from ...dependencies.query_filter import create_filter_extractor +from ...dependencies.query_filter import MetadataFilter, create_filter_extractor from ...models.docs.list_docs import list_docs as list_docs_query from .router import router @@ -13,10 +13,11 @@ @router.get("/users/{user_id}/docs", tags=["docs"]) async def list_user_docs( x_developer_id: Annotated[UUID, Depends(get_developer_id)], + user_id: UUID, metadata_filter: Annotated[ - dict, Depends(create_filter_extractor("metadata_filter")) + MetadataFilter, + Depends(create_filter_extractor("metadata_filter")), ], - user_id: UUID, limit: int = 100, offset: int = 0, sort_by: Literal["created_at", "updated_at"] = "created_at", @@ -30,7 +31,7 @@ async def list_user_docs( offset=offset, sort_by=sort_by, direction=direction, - metadata_filter=metadata_filter or {}, + metadata_filter=metadata_filter.model_dump(mode="json"), ) return ListResponse[Doc](items=docs) @@ -39,10 +40,10 @@ async def list_user_docs( @router.get("/agents/{agent_id}/docs", tags=["docs"]) async def list_agent_docs( x_developer_id: Annotated[UUID, Depends(get_developer_id)], + agent_id: UUID, metadata_filter: Annotated[ - dict, Depends(create_filter_extractor("metadata_filter")) + MetadataFilter, Depends(create_filter_extractor("metadata_filter")) ], - agent_id: UUID, limit: int = 100, offset: int = 0, sort_by: Literal["created_at", "updated_at"] = "created_at", @@ -56,7 +57,7 @@ async def list_agent_docs( offset=offset, sort_by=sort_by, direction=direction, - metadata_filter=metadata_filter or {}, + metadata_filter=metadata_filter.model_dump(mode="json"), ) return ListResponse[Doc](items=docs) diff --git a/agents-api/agents_api/routers/docs/search_docs.py b/agents-api/agents_api/routers/docs/search_docs.py index ce0b62811..22bba86a1 100644 --- a/agents-api/agents_api/routers/docs/search_docs.py +++ b/agents-api/agents_api/routers/docs/search_docs.py @@ -2,15 +2,18 @@ from typing import Annotated, Any, Dict, List, Optional, Tuple, Union from uuid import UUID +import numpy as np from fastapi import Depends from ...autogen.openapi_model import ( + DocReference, DocSearchResponse, HybridDocSearchRequest, TextOnlyDocSearchRequest, VectorDocSearchRequest, ) from ...dependencies.developer_id import get_developer_id +from ...models.docs.mmr import maximal_marginal_relevance from ...models.docs.search_docs_by_embedding import search_docs_by_embedding from ...models.docs.search_docs_by_text import search_docs_by_text from ...models.docs.search_docs_hybrid import search_docs_hybrid @@ -25,21 +28,28 @@ def get_search_fn_and_params( search_fn, params = None, None match search_params: - case TextOnlyDocSearchRequest(text=query, limit=k): + case TextOnlyDocSearchRequest( + text=query, limit=k, metadata_filter=metadata_filter + ): search_fn = search_docs_by_text params = dict( query=query, k=k, + metadata_filter=metadata_filter, ) case VectorDocSearchRequest( - vector=query_embedding, limit=k, confidence=confidence + vector=query_embedding, + limit=k, + confidence=confidence, + metadata_filter=metadata_filter, ): search_fn = search_docs_by_embedding params = dict( query_embedding=query_embedding, - k=k, + k=k * 3 if search_params.mmr_strength > 0 else k, confidence=confidence, + metadata_filter=metadata_filter, ) case HybridDocSearchRequest( @@ -48,14 +58,16 @@ def get_search_fn_and_params( limit=k, confidence=confidence, alpha=alpha, + metadata_filter=metadata_filter, ): search_fn = search_docs_hybrid params = dict( query=query, query_embedding=query_embedding, - k=k, + k=k * 3 if search_params.mmr_strength > 0 else k, embed_search_options=dict(confidence=confidence), alpha=alpha, + metadata_filter=metadata_filter, ) return search_fn, params @@ -69,15 +81,40 @@ async def search_user_docs( ), user_id: UUID, ) -> DocSearchResponse: + """ + Searches for documents associated with a specific user. + + Parameters: + x_developer_id (UUID): The unique identifier of the developer associated with the user. + search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search. + user_id (UUID): The unique identifier of the user associated with the documents. + + Returns: + DocSearchResponse: The search results. + """ + + # MMR here search_fn, params = get_search_fn_and_params(search_params) start = time.time() - docs = search_fn( + docs: list[DocReference] = search_fn( developer_id=x_developer_id, owners=[("user", user_id)], **params, ) + if ( + not isinstance(search_params, TextOnlyDocSearchRequest) + and search_params.mmr_strength > 0 + and len(docs) > search_params.limit + ): + indices = maximal_marginal_relevance( + np.asarray(params["query_embedding"]), + [doc.snippet.embedding for doc in docs], + k=search_params.limit, + ) + docs = [doc for i, doc in enumerate(docs) if i in set(indices)] + end = time.time() time_taken = end - start @@ -96,15 +133,39 @@ async def search_agent_docs( ), agent_id: UUID, ) -> DocSearchResponse: + """ + Searches for documents associated with a specific agent. + + Parameters: + x_developer_id (UUID): The unique identifier of the developer associated with the agent. + search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search. + agent_id (UUID): The unique identifier of the agent associated with the documents. + + Returns: + DocSearchResponse: The search results. + """ + search_fn, params = get_search_fn_and_params(search_params) start = time.time() - docs = search_fn( + docs: list[DocReference] = search_fn( developer_id=x_developer_id, owners=[("agent", agent_id)], **params, ) + if ( + not isinstance(search_params, TextOnlyDocSearchRequest) + and search_params.mmr_strength > 0 + and len(docs) > search_params.limit + ): + indices = maximal_marginal_relevance( + np.asarray(params["query_embedding"]), + [doc.snippet.embedding for doc in docs], + k=search_params.limit, + ) + docs = [doc for i, doc in enumerate(docs) if i in set(indices)] + end = time.time() time_taken = end - start diff --git a/agents-api/agents_api/routers/files/__init__.py b/agents-api/agents_api/routers/files/__init__.py new file mode 100644 index 000000000..5e3d5a62c --- /dev/null +++ b/agents-api/agents_api/routers/files/__init__.py @@ -0,0 +1,6 @@ +# ruff: noqa: F401 + +from .create_file import create_file +from .delete_file import delete_file +from .get_file import get_file +from .router import router diff --git a/agents-api/agents_api/routers/files/create_file.py b/agents-api/agents_api/routers/files/create_file.py new file mode 100644 index 000000000..80d80e6f3 --- /dev/null +++ b/agents-api/agents_api/routers/files/create_file.py @@ -0,0 +1,44 @@ +import base64 +from typing import Annotated +from uuid import UUID + +from fastapi import Depends +from starlette.status import HTTP_201_CREATED + +from ...autogen.openapi_model import ( + CreateFileRequest, + File, + ResourceCreatedResponse, +) +from ...clients import async_s3 +from ...dependencies.developer_id import get_developer_id +from ...models.files.create_file import create_file as create_file_query +from .router import router + + +async def upload_file_content(file_id: UUID, content: str) -> None: + """Upload file content to blob storage using the file ID as the key""" + await async_s3.setup() + key = str(file_id) + content_bytes = base64.b64decode(content) + await async_s3.add_object(key, content_bytes) + + +@router.post("/files", status_code=HTTP_201_CREATED, tags=["files"]) +async def create_file( + x_developer_id: Annotated[UUID, Depends(get_developer_id)], + data: CreateFileRequest, +) -> ResourceCreatedResponse: + file: File = create_file_query( + developer_id=x_developer_id, + data=data, + ) + + # Upload the file content to blob storage using the file ID as the key + await upload_file_content(file.id, data.content) + + return ResourceCreatedResponse( + id=file.id, + created_at=file.created_at, + jobs=[], + ) diff --git a/agents-api/agents_api/routers/files/delete_file.py b/agents-api/agents_api/routers/files/delete_file.py new file mode 100644 index 000000000..fbe10290e --- /dev/null +++ b/agents-api/agents_api/routers/files/delete_file.py @@ -0,0 +1,30 @@ +from typing import Annotated +from uuid import UUID + +from fastapi import Depends +from starlette.status import HTTP_202_ACCEPTED + +from ...autogen.openapi_model import ResourceDeletedResponse +from ...clients import async_s3 +from ...dependencies.developer_id import get_developer_id +from ...models.files.delete_file import delete_file as delete_file_query +from .router import router + + +async def delete_file_content(file_id: UUID) -> None: + """Delete file content from blob storage using the file ID as the key""" + await async_s3.setup() + key = str(file_id) + await async_s3.delete_object(key) + + +@router.delete("/files/{file_id}", status_code=HTTP_202_ACCEPTED, tags=["files"]) +async def delete_file( + file_id: UUID, x_developer_id: Annotated[UUID, Depends(get_developer_id)] +) -> ResourceDeletedResponse: + resource_deleted = delete_file_query(developer_id=x_developer_id, file_id=file_id) + + # Delete the file content from blob storage + await delete_file_content(file_id) + + return resource_deleted diff --git a/agents-api/agents_api/routers/files/get_file.py b/agents-api/agents_api/routers/files/get_file.py new file mode 100644 index 000000000..cc5dcdc35 --- /dev/null +++ b/agents-api/agents_api/routers/files/get_file.py @@ -0,0 +1,31 @@ +import base64 +from typing import Annotated +from uuid import UUID + +from fastapi import Depends + +from ...autogen.openapi_model import File +from ...clients import async_s3 +from ...dependencies.developer_id import get_developer_id +from ...models.files.get_file import get_file as get_file_query +from .router import router + + +async def fetch_file_content(file_id: UUID) -> str: + """Fetch file content from blob storage using the file ID as the key""" + await async_s3.setup() + key = str(file_id) + content = await async_s3.get_object(key) + return base64.b64encode(content).decode("utf-8") + + +@router.get("/files/{file_id}", tags=["files"]) +async def get_file( + file_id: UUID, x_developer_id: Annotated[UUID, Depends(get_developer_id)] +) -> File: + file = get_file_query(developer_id=x_developer_id, file_id=file_id) + + # Fetch the file content from blob storage + file.content = await fetch_file_content(file.id) + + return file diff --git a/agents-api/agents_api/routers/files/router.py b/agents-api/agents_api/routers/files/router.py new file mode 100644 index 000000000..5c3ec9311 --- /dev/null +++ b/agents-api/agents_api/routers/files/router.py @@ -0,0 +1,3 @@ +from fastapi import APIRouter + +router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/internal/__init__.py b/agents-api/agents_api/routers/internal/__init__.py index 237804332..df76d5dc3 100644 --- a/agents-api/agents_api/routers/internal/__init__.py +++ b/agents-api/agents_api/routers/internal/__init__.py @@ -1 +1 @@ -from .router import router +from .router import router as router diff --git a/agents-api/agents_api/routers/internal/router.py b/agents-api/agents_api/routers/internal/router.py index 3293f2463..04addc6f5 100644 --- a/agents-api/agents_api/routers/internal/router.py +++ b/agents-api/agents_api/routers/internal/router.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request, Response +from fastapi import APIRouter, Request from google.protobuf import json_format from temporalio.api.common.v1 import Payloads @@ -11,18 +11,43 @@ # Decode route @router.post("/temporal/decode", tags=["temporal"]) -async def decode_payloads(req: Request) -> dict: +async def decode_payloads(req: Request): + """Decodes a list of payloads from the request body. + + Args: + req (Request): The request containing the payloads to decode. + + Returns: + dict: A dictionary containing the decoded payloads, including any errors encountered. + """ body = json_format.Parse(await req.body(), Payloads()) payloads = body.payloads - decoded_payloads = [] for p in payloads: + # Attempt to decode the payload data = converter.from_payload(p) + # Handle Pydantic models if hasattr(data, "model_dump"): data = data.model_dump() - decoded_payloads.append({"data": data, "metadata": p.metadata}) + # Handle bytes data + elif isinstance(data, bytes): + try: + data = data.decode("utf-8") + except UnicodeDecodeError: + # If it's not UTF-8 encoded text, leave it as bytes + data = str(data) + + # Convert metadata more safely + metadata = {} + for k, v in p.metadata.items(): + try: + metadata[k] = v.decode() if isinstance(v, bytes) else v + except UnicodeDecodeError: + metadata[k] = str(v) + + decoded_payloads.append({"data": data, "metadata": metadata}) return {"payloads": decoded_payloads} diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index 1963db316..c0ccc2912 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -1,7 +1,7 @@ from typing import Annotated, Optional from uuid import UUID, uuid4 -from fastapi import BackgroundTasks, Depends, Header +from fastapi import BackgroundTasks, Depends, Header, HTTPException, status from starlette.status import HTTP_201_CREATED from ...autogen.openapi_model import ( @@ -17,11 +17,18 @@ from ...common.utils.datetime import utcnow from ...common.utils.template import render_template from ...dependencies.developer_id import get_developer_data +from ...env import max_free_sessions from ...models.chat.gather_messages import gather_messages from ...models.chat.prepare_chat_context import prepare_chat_context from ...models.entry.create_entries import create_entries +from ...models.session.count_sessions import count_sessions as count_sessions_query +from .metrics import total_tokens_per_user from .router import router +COMPUTER_USE_BETA_FLAG = "computer-use-2024-10-22" + +COMPUTER_USE_BETA_FLAG = "computer-use-2024-10-22" + @router.post( "/sessions/{session_id}/chat", @@ -35,6 +42,42 @@ async def chat( background_tasks: BackgroundTasks, x_custom_api_key: Optional[str] = Header(None, alias="X-Custom-Api-Key"), ) -> ChatResponse: + """ + Initiates a chat session. + + Parameters: + developer (Developer): The developer associated with the chat session. + session_id (UUID): The unique identifier of the chat session. + chat_input (ChatInput): The chat input data. + background_tasks (BackgroundTasks): The background tasks to run. + x_custom_api_key (Optional[str]): The custom API key. + + Returns: + ChatResponse: The chat response. + """ + + # check if the developer is paid + if "paid" not in developer.tags: + # get the session length + sessions = count_sessions_query(developer_id=developer.id) + session_length = sessions["count"] + if session_length > max_free_sessions: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Session length exceeded the free tier limit", + ) + + # check if the developer is paid + if "paid" not in developer.tags: + # get the session length + sessions = count_sessions_query(developer_id=developer.id) + session_length = sessions["count"] + if session_length > max_free_sessions: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Session length exceeded the free tier limit", + ) + if chat_input.stream: raise NotImplementedError("Streaming is not yet implemented") @@ -46,7 +89,7 @@ async def chat( # Merge the settings and prepare environment chat_context.merge_settings(chat_input) - settings: dict = chat_context.settings.model_dump() + settings: dict = chat_context.settings.model_dump(mode="json", exclude_none=True) # Get the past messages and doc references past_messages, doc_references = await gather_messages( @@ -61,7 +104,7 @@ async def chat( env["docs"] = [ dict( title=ref.title, - content=[snippet.content for snippet in ref.snippets], + content=[ref.snippet.content], ) for ref in doc_references ] @@ -79,7 +122,7 @@ async def chat( past_messages = system_messages + past_messages # Render the incoming messages - new_raw_messages = [msg.model_dump() for msg in chat_input.messages] + new_raw_messages = [msg.model_dump(mode="json") for msg in chat_input.messages] if chat_context.session.render_templates: new_messages = await render_template(new_raw_messages, variables=env) @@ -92,6 +135,22 @@ async def chat( # Get the tools tools = settings.get("tools") or chat_context.get_active_tools() + # Check if using Claude model and has specific tool types + is_claude_model = settings["model"].lower().startswith("claude-3.5") + + # Format tools for litellm + # formatted_tools = ( + # tools if is_claude_model else [format_tool(tool) for tool in tools] + # ) + + # Check if using Claude model and has specific tool types + is_claude_model = settings["model"].lower().startswith("claude-3.5") + + # Format tools for litellm + # formatted_tools = ( + # tools if is_claude_model else [format_tool(tool) for tool in tools] + # ) + # FIXME: Truncate chat messages in the chat context # SCRUM-7 if chat_context.session.context_overflow == "truncate": @@ -100,19 +159,61 @@ async def chat( # FIXME: Hotfix for datetime not serializable. Needs investigation messages = [ - msg.model_dump() if hasattr(msg, "model_dump") else msg for msg in messages + { + k: v + for k, v in m.items() + if k in ["role", "content", "tool_calls", "tool_call_id", "user"] + } + for m in messages ] - messages = [ - dict(role=m["role"], content=m["content"], user=m.get("user")) for m in messages - ] + # FIXME: Hack to make the computer use tools compatible with litellm + # Issue was: litellm expects type to be `computer_20241022` and spec to be + # `function` (see: https://docs.litellm.ai/docs/providers/anthropic#computer-tools) + # but we don't allow that (spec should match type). + formatted_tools = [] + for i, tool in enumerate(tools): + if tool.type == "computer_20241022" and tool.computer_20241022: + function = tool.computer_20241022 + tool = { + "type": tool.type, + "function": { + "name": tool.name, + "parameters": { + k: v + for k, v in function.model_dump().items() + if k not in ["name", "type"] + } + if function is not None + else {}, + }, + } + formatted_tools.append(tool) - # Get the response from the model + # If not using Claude model + # FIXME: Enable formatted_tools once format-tools PR is merged. + if not is_claude_model: + formatted_tools = None + + # HOTFIX: for groq calls, litellm expects tool_calls_id not to be in the messages + # FIXME: This is a temporary fix. We need to update the agent-api to use the new tool calling format + is_groq_model = settings["model"].lower().startswith("llama-3.1") + if is_groq_model: + messages = [ + { + k: v + for k, v in message.items() + if k not in ["tool_calls", "tool_call_id", "user", "continue_", "name"] + } + for message in messages + ] + + # Use litellm for other models model_response = await litellm.acompletion( messages=messages, - tools=tools or None, - user=str(developer.id), # For tracking usage - tags=developer.tags, # For filtering models in litellm + tools=formatted_tools or None, + user=str(developer.id), + tags=developer.tags, custom_api_key=x_custom_api_key, **settings, ) @@ -166,4 +267,10 @@ async def chat( choices=[choice.model_dump() for choice in model_response.choices], ) + total_tokens_per_user.labels(str(developer.id)).inc( + amount=chat_response.usage.total_tokens + if chat_response.usage is not None + else 0 + ) + return chat_response diff --git a/agents-api/agents_api/routers/sessions/list_sessions.py b/agents-api/agents_api/routers/sessions/list_sessions.py index 6a4555e6e..fc9cd2e99 100644 --- a/agents-api/agents_api/routers/sessions/list_sessions.py +++ b/agents-api/agents_api/routers/sessions/list_sessions.py @@ -5,7 +5,7 @@ from ...autogen.openapi_model import ListResponse, Session from ...dependencies.developer_id import get_developer_id -from ...dependencies.query_filter import create_filter_extractor +from ...dependencies.query_filter import MetadataFilter, create_filter_extractor from ...models.session.list_sessions import list_sessions as list_sessions_query from .router import router @@ -14,8 +14,8 @@ async def list_sessions( x_developer_id: Annotated[UUID, Depends(get_developer_id)], metadata_filter: Annotated[ - dict, Depends(create_filter_extractor("metadata_filter")) - ] = {}, + MetadataFilter, Depends(create_filter_extractor("metadata_filter")) + ] = MetadataFilter(), limit: int = 100, offset: int = 0, sort_by: Literal["created_at", "updated_at"] = "created_at", @@ -27,7 +27,7 @@ async def list_sessions( offset=offset, sort_by=sort_by, direction=direction, - metadata_filter=metadata_filter or {}, + metadata_filter=metadata_filter.model_dump(mode="json") or {}, ) return ListResponse[Session](items=sessions) diff --git a/agents-api/agents_api/routers/sessions/metrics.py b/agents-api/agents_api/routers/sessions/metrics.py new file mode 100644 index 000000000..5c432e4e7 --- /dev/null +++ b/agents-api/agents_api/routers/sessions/metrics.py @@ -0,0 +1,7 @@ +from prometheus_client import Counter + +total_tokens_per_user = Counter( + "total_tokens_per_user", + "Total token count per user", + labelnames=("developer_id",), +) diff --git a/agents-api/agents_api/routers/tasks/create_or_update_task.py b/agents-api/agents_api/routers/tasks/create_or_update_task.py index 50dbf19d9..f40530dfc 100644 --- a/agents-api/agents_api/routers/tasks/create_or_update_task.py +++ b/agents-api/agents_api/routers/tasks/create_or_update_task.py @@ -29,10 +29,6 @@ async def create_or_update_task( # TODO: Do thorough validation of the task spec # SCRUM-10 - # FIXME: There is also some subtle bug here that prevents us from - # starting executions from tasks created via this endpoint - # SCRUM-9 - # Validate the input schema try: if data.input_schema is not None: @@ -44,11 +40,9 @@ async def create_or_update_task( except ValidationError: pass - task = create_or_update_task_query( + return create_or_update_task_query( developer_id=x_developer_id, agent_id=agent_id, task_id=task_id, data=data, ) - - return ResourceUpdatedResponse(id=task.id, updated_at=task.updated_at, jobs=[]) diff --git a/agents-api/agents_api/routers/tasks/create_task.py b/agents-api/agents_api/routers/tasks/create_task.py index d92bd4a4d..0e233ac97 100644 --- a/agents-api/agents_api/routers/tasks/create_task.py +++ b/agents-api/agents_api/routers/tasks/create_task.py @@ -23,7 +23,6 @@ async def create_task( ) -> ResourceCreatedResponse: # TODO: Do thorough validation of the task spec # SCRUM-10 - # TODO: Validate the jinja templates # Validate the input schema try: @@ -36,10 +35,8 @@ async def create_task( except ValidationError: pass - task = create_task_query( + return create_task_query( developer_id=x_developer_id, agent_id=agent_id, data=data, ) - - return ResourceCreatedResponse(id=task.id, created_at=task.created_at, jobs=[]) diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 24de79522..09342bf84 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -10,14 +10,20 @@ from starlette.status import HTTP_201_CREATED from temporalio.client import WorkflowHandle -from ...autogen.Executions import Execution from ...autogen.openapi_model import ( CreateExecutionRequest, + Execution, ResourceCreatedResponse, UpdateExecutionRequest, ) from ...clients.temporal import run_task_execution_workflow +from ...common.protocol.developers import Developer from ...dependencies.developer_id import get_developer_id +from ...env import max_free_executions +from ...models.developer.get_developer import get_developer +from ...models.execution.count_executions import ( + count_executions as count_executions_query, +) from ...models.execution.create_execution import ( create_execution as create_execution_query, ) @@ -113,6 +119,22 @@ async def create_task_execution( raise + # get developer data + developer: Developer = get_developer(developer_id=x_developer_id) + + # # check if the developer is paid + if "paid" not in developer.tags: + executions = count_executions_query( + developer_id=x_developer_id, task_id=task_id + ) + + execution_count = executions["count"] + if execution_count > max_free_executions: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Execution count exceeded the free tier limit", + ) + execution, handle = await start_execution( developer_id=x_developer_id, task_id=task_id, diff --git a/agents-api/agents_api/routers/tasks/router.py b/agents-api/agents_api/routers/tasks/router.py index 9a702c15a..101dcb228 100644 --- a/agents-api/agents_api/routers/tasks/router.py +++ b/agents-api/agents_api/routers/tasks/router.py @@ -1,9 +1,10 @@ from typing import Callable -import yaml from fastapi import APIRouter, Request, Response from fastapi.routing import APIRoute +from ...common.utils import yaml + class YamlRequest(Request): async def body(self) -> bytes: @@ -15,7 +16,7 @@ async def body(self) -> bytes: "application/yaml", "text/yaml", ]: - body = yaml.load(body, yaml.CSafeLoader) + body = yaml.load(body) self._body = body diff --git a/agents-api/agents_api/routers/tasks/update_execution.py b/agents-api/agents_api/routers/tasks/update_execution.py index d887c455d..e88c36ed9 100644 --- a/agents-api/agents_api/routers/tasks/update_execution.py +++ b/agents-api/agents_api/routers/tasks/update_execution.py @@ -34,7 +34,7 @@ async def update_execution( *get_temporal_workflow_data(execution_id=execution_id) ) await wf_handle.cancel() - except Exception as e: + except Exception: raise HTTPException(status_code=500, detail="Failed to stop execution") case ResumeExecutionRequest(): @@ -59,7 +59,7 @@ async def update_execution( ) try: await act_handle.complete(data.input) - except Exception as e: + except Exception: raise HTTPException( status_code=500, detail="Failed to resume execution" ) diff --git a/agents-api/agents_api/routers/users/list_users.py b/agents-api/agents_api/routers/users/list_users.py index a234d8a5f..926699d40 100644 --- a/agents-api/agents_api/routers/users/list_users.py +++ b/agents-api/agents_api/routers/users/list_users.py @@ -5,7 +5,7 @@ from ...autogen.openapi_model import ListResponse, User from ...dependencies.developer_id import get_developer_id -from ...dependencies.query_filter import create_filter_extractor +from ...dependencies.query_filter import MetadataFilter, create_filter_extractor from ...models.user.list_users import list_users as list_users_query from .router import router @@ -14,7 +14,7 @@ async def list_users( x_developer_id: Annotated[UUID, Depends(get_developer_id)], metadata_filter: Annotated[ - dict, Depends(create_filter_extractor("metadata_filter")) + MetadataFilter, Depends(create_filter_extractor("metadata_filter")) ], limit: int = 100, offset: int = 0, @@ -27,7 +27,7 @@ async def list_users( offset=offset, sort_by=sort_by, direction=direction, - metadata_filter=metadata_filter or {}, + metadata_filter=metadata_filter.model_dump(mode="json") or {}, ) return ListResponse[User](items=users) diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index 048887f68..a9f191441 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -2,18 +2,21 @@ This module initializes the FastAPI application, registers routes, sets up middleware, and configures exception handlers. """ +import asyncio import logging -from typing import Any, Callable +from typing import Any, Callable, Union, cast -import fire import sentry_sdk import uvicorn +import uvloop from fastapi import APIRouter, Depends, FastAPI, Request, status from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from litellm.exceptions import APIError +from prometheus_fastapi_instrumentator import Instrumentator from pycozo.client import QueryException +from pydantic import ValidationError from scalar_fastapi import get_scalar_api_reference from temporalio.service import RPCError @@ -24,6 +27,7 @@ from .routers import ( agents, docs, + files, internal, jobs, sessions, @@ -43,22 +47,77 @@ logger: logging.Logger = logging.getLogger(__name__) -def make_exception_handler(status: int) -> Callable[[Any, Any], Any]: +def make_exception_handler(status_code: int) -> Callable[[Any, Any], Any]: """ Creates a custom exception handler for the application. Parameters: - - status (int): The HTTP status code to return for this exception. + - status_code (int): The HTTP status code to return for this exception. Returns: A callable exception handler that logs the exception and returns a JSON response with the specified status code. """ - async def _handler(request: Request, exc): - exc_str = f"{exc}".replace("\n", " ").replace(" ", " ") - logger.exception(exc) - content = {"status_code": status, "message": exc_str, "data": None} - return JSONResponse(content=content, status_code=status) + async def _handler(request: Request, exc: Exception): + location = None + offending_input = None + + # Return the deepest matching possibility + if isinstance(exc, (ValidationError, RequestValidationError)): + exc = cast(Union[ValidationError, RequestValidationError], exc) + errors = exc.errors() + + # Get the deepest matching errors + max_depth = max(len(error["loc"]) for error in errors) + errors = [error for error in errors if len(error["loc"]) == max_depth] + + # Get the common location + location = errors[0]["loc"] + for error in errors[1:]: + for a, b in zip(location, error["loc"]): + if a == b: + continue + location = location[:-1] + break + + location = location[1:] # Skip the first element ("body") + + # Get the part of the input that caused the error + offending_input = exc.body + for loc in location: + match offending_input: + case dict(): + if loc not in offending_input: + break + case list(): + if not ( + isinstance(loc, int) and 0 <= loc < len(offending_input) + ): + break + case _: + break + + offending_input = offending_input[loc] + + # Keep only the message from the error + errors = [ + error.get("msg", error) + if isinstance(error, dict) + else getattr(error, "msg", error) + for error in errors + ] + + else: + errors = exc.errors() if hasattr(exc, "errors") else [exc] + + return JSONResponse( + content={ + "errors": errors, + "offending_input": offending_input, + "location": location, + }, + status_code=status_code, + ) return _handler @@ -100,6 +159,9 @@ def register_exceptions(app: FastAPI) -> None: root_path=api_prefix, ) +# Enable metrics +Instrumentator().instrument(app).expose(app, include_in_schema=False) + # Create a new router for the docs scalar_router = APIRouter() @@ -121,6 +183,7 @@ async def scalar_html(): app.include_router(sessions.router, dependencies=[Depends(get_api_key)]) app.include_router(users.router, dependencies=[Depends(get_api_key)]) app.include_router(jobs.router, dependencies=[Depends(get_api_key)]) +app.include_router(files.router, dependencies=[Depends(get_api_key)]) app.include_router(docs.router, dependencies=[Depends(get_api_key)]) app.include_router(tasks.router, dependencies=[Depends(get_api_key)]) app.include_router(internal.router) @@ -203,6 +266,4 @@ def main( ) -# Check if the script is being run directly and, if so, start the Uvicorn server with the specified configuration. -if __name__ == "__main__": - fire.Fire(main) +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) diff --git a/agents-api/agents_api/worker/codec.py b/agents-api/agents_api/worker/codec.py index 611d7b8f1..d562b4653 100644 --- a/agents-api/agents_api/worker/codec.py +++ b/agents-api/agents_api/worker/codec.py @@ -6,10 +6,11 @@ import dataclasses import logging -import pickle import sys +import time from typing import Any, Optional, Type +import larch.pickle as pickle import temporalio.converter # from beartype import BeartypeConf @@ -24,11 +25,31 @@ def serialize(x: Any) -> bytes: - return compress(pickle.dumps(x, protocol=pickle.HIGHEST_PROTOCOL)) + start_time = time.time() + pickled = pickle.dumps(x, protocol=-1) + compressed = compress(pickled) + + duration = time.time() - start_time + if duration > 1: + print( + f"||| [SERIALIZE] Time taken: {duration}s // Object size: {sys.getsizeof(x) / 1000}kb" + ) + + return compressed def deserialize(b: bytes) -> Any: - return pickle.loads(decompress(b)) + start_time = time.time() + decompressed = decompress(b) + object = pickle.loads(decompressed) + + duration = time.time() - start_time + if duration > 1: + print( + f"||| [DESERIALIZE] Time taken: {duration}s // Object size: {sys.getsizeof(b) / 1000}kb" + ) + + return object def from_payload_data(data: bytes, type_hint: Optional[Type] = None) -> Any: @@ -108,10 +129,21 @@ def from_payload(self, payload: Payload, type_hint: Optional[Type] = None) -> An f"{sys.version_info.major}.{sys.version_info.minor}".encode() ) - assert payload.metadata["encoding"] == self.b_encoding - assert payload.metadata["python_version"] == current_python_version + # Check if this is a payload we can handle + if ( + "encoding" not in payload.metadata + or payload.metadata["encoding"] != self.b_encoding + or "python_version" not in payload.metadata + or payload.metadata["python_version"] != current_python_version + ): + # Return the payload data as-is if we can't handle it + return payload.data - return from_payload_data(payload.data, type_hint) + try: + return from_payload_data(payload.data, type_hint) + except Exception as e: + logging.warning(f"Failed to decode payload with our encoder: {e}") + return None class PydanticPayloadConverter(CompositePayloadConverter): diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index dc02cb4a7..359c92433 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -5,6 +5,9 @@ from temporalio.client import Client from temporalio.worker import Worker +MAX_CONCURRENT_WORKFLOW_TASKS = None # default None +MAX_CONCURRENT_ACTIVITIES = 100 # default None + def create_worker(client: Client) -> Any: """ @@ -21,7 +24,9 @@ def create_worker(client: Client) -> Any: from ..activities.mem_mgmt import mem_mgmt from ..activities.mem_rating import mem_rating from ..activities.summarization import summarization + from ..activities.sync_items_remote import load_inputs_remote, save_inputs_remote from ..activities.truncation import truncation + from ..common.interceptors import CustomInterceptor from ..env import ( temporal_task_queue, ) @@ -60,7 +65,12 @@ def create_worker(client: Client) -> Any: mem_rating, summarization, truncation, + save_inputs_remote, + load_inputs_remote, ], + interceptors=[CustomInterceptor()], + max_concurrent_workflow_tasks=MAX_CONCURRENT_WORKFLOW_TASKS, + max_concurrent_activities=MAX_CONCURRENT_ACTIVITIES, ) return worker diff --git a/agents-api/agents_api/workflows/embed_docs.py b/agents-api/agents_api/workflows/embed_docs.py index 83eefe907..bb61a55e1 100644 --- a/agents-api/agents_api/workflows/embed_docs.py +++ b/agents-api/agents_api/workflows/embed_docs.py @@ -9,6 +9,7 @@ from ..activities.embed_docs import embed_docs from ..activities.types import EmbedDocsPayload from ..common.retry_policies import DEFAULT_RETRY_POLICY + from ..env import temporal_schedule_to_close_timeout @workflow.defn @@ -18,6 +19,8 @@ async def run(self, embed_payload: EmbedDocsPayload) -> None: await workflow.execute_activity( embed_docs, embed_payload, - schedule_to_close_timeout=timedelta(seconds=600), + schedule_to_close_timeout=timedelta( + seconds=temporal_schedule_to_close_timeout + ), retry_policy=DEFAULT_RETRY_POLICY, ) diff --git a/agents-api/agents_api/workflows/mem_mgmt.py b/agents-api/agents_api/workflows/mem_mgmt.py index 31c973741..78e8d4b45 100644 --- a/agents-api/agents_api/workflows/mem_mgmt.py +++ b/agents-api/agents_api/workflows/mem_mgmt.py @@ -8,6 +8,7 @@ with workflow.unsafe.imports_passed_through(): from ..activities.mem_mgmt import mem_mgmt from ..autogen.openapi_model import InputChatMLMessage + from ..env import temporal_schedule_to_close_timeout @workflow.defn @@ -22,5 +23,7 @@ async def run( return await workflow.execute_activity( mem_mgmt, [dialog, session_id, previous_memories], - schedule_to_close_timeout=timedelta(seconds=600), + schedule_to_close_timeout=timedelta( + seconds=temporal_schedule_to_close_timeout + ), ) diff --git a/agents-api/agents_api/workflows/mem_rating.py b/agents-api/agents_api/workflows/mem_rating.py index 0a87fd787..a12f020c1 100644 --- a/agents-api/agents_api/workflows/mem_rating.py +++ b/agents-api/agents_api/workflows/mem_rating.py @@ -8,6 +8,7 @@ with workflow.unsafe.imports_passed_through(): from ..activities.mem_rating import mem_rating from ..common.retry_policies import DEFAULT_RETRY_POLICY + from ..env import temporal_schedule_to_close_timeout @workflow.defn @@ -17,6 +18,8 @@ async def run(self, memory: str) -> None: return await workflow.execute_activity( mem_rating, memory, - schedule_to_close_timeout=timedelta(seconds=600), + schedule_to_close_timeout=timedelta( + seconds=temporal_schedule_to_close_timeout + ), retry_policy=DEFAULT_RETRY_POLICY, ) diff --git a/agents-api/agents_api/workflows/summarization.py b/agents-api/agents_api/workflows/summarization.py index 96ce4c460..8f724d913 100644 --- a/agents-api/agents_api/workflows/summarization.py +++ b/agents-api/agents_api/workflows/summarization.py @@ -8,6 +8,7 @@ with workflow.unsafe.imports_passed_through(): from ..activities.summarization import summarization from ..common.retry_policies import DEFAULT_RETRY_POLICY + from ..env import temporal_schedule_to_close_timeout @workflow.defn @@ -17,6 +18,8 @@ async def run(self, session_id: str) -> None: return await workflow.execute_activity( summarization, session_id, - schedule_to_close_timeout=timedelta(seconds=600), + schedule_to_close_timeout=timedelta( + seconds=temporal_schedule_to_close_timeout + ), retry_policy=DEFAULT_RETRY_POLICY, ) diff --git a/agents-api/agents_api/workflows/task_execution/__init__.py b/agents-api/agents_api/workflows/task_execution/__init__.py index edf54fb12..5ec68713b 100644 --- a/agents-api/agents_api/workflows/task_execution/__init__.py +++ b/agents-api/agents_api/workflows/task_execution/__init__.py @@ -4,25 +4,27 @@ from datetime import timedelta from typing import Any -from pydantic import RootModel from temporalio import workflow from temporalio.exceptions import ApplicationError # Import necessary modules and types with workflow.unsafe.imports_passed_through(): + from pydantic import RootModel + from ...activities import task_steps from ...activities.excecute_api_call import execute_api_call from ...activities.execute_integration import execute_integration from ...activities.execute_system import execute_system + from ...activities.sync_items_remote import load_inputs_remote, save_inputs_remote from ...autogen.openapi_model import ( ApiCallDef, + BaseIntegrationDef, ErrorWorkflowStep, EvaluateStep, ForeachDo, ForeachStep, GetStep, IfElseWorkflowStep, - IntegrationDef, LogStep, MapReduceStep, ParallelStep, @@ -32,13 +34,13 @@ SleepFor, SleepStep, SwitchStep, + SystemDef, ToolCallStep, TransitionTarget, WaitForInputStep, WorkflowStep, YieldStep, ) - from ...autogen.Tools import SystemDef from ...common.protocol.tasks import ( ExecutionInput, PartialTransition, @@ -46,7 +48,7 @@ StepOutcome, ) from ...common.retry_policies import DEFAULT_RETRY_POLICY - from ...env import debug, testing + from ...env import debug, temporal_schedule_to_close_timeout, testing from .helpers import ( continue_as_child, execute_foreach_step, @@ -118,47 +120,20 @@ # Main workflow definition @workflow.defn class TaskExecutionWorkflow: - user_state: dict[str, Any] = {} - - def __init__(self) -> None: - self.user_state = {} - - # TODO: Add endpoints for getting and setting user state for an execution - # Query methods for user state - @workflow.query - def get_user_state(self) -> dict[str, Any]: - return self.user_state - - @workflow.query - def get_user_state_by_key(self, key: str) -> Any: - return self.user_state.get(key) - - # Signal methods for updating user state - @workflow.signal - def set_user_state(self, key: str, value: Any) -> None: - self.user_state[key] = value - - @workflow.signal - def update_user_state(self, values: dict[str, Any]) -> None: - self.user_state.update(values) - # Main workflow run method @workflow.run async def run( self, execution_input: ExecutionInput, start: TransitionTarget = TransitionTarget(workflow="main", step=0), - previous_inputs: list[Any] = [], - user_state: dict[str, Any] = {}, + previous_inputs: list | None = None, ) -> Any: - # Set the initial user state - self.user_state = user_state - workflow.logger.info( f"TaskExecutionWorkflow for task {execution_input.task.id}" f" [LOC {start.workflow}.{start.step}]" ) + # FIXME: Look into saving arguments to the blob store if necessary # 0. Prepare context previous_inputs = previous_inputs or [execution_input.arguments] @@ -172,8 +147,10 @@ async def run( # --- + continued_as_new = workflow.info().continued_run_id is not None + # 1. Transition to starting if not done yet - if context.is_first_step: + if context.is_first_step and not continued_as_new: await transition( context, type="init" if context.is_main else "init_branch", @@ -200,7 +177,9 @@ async def run( context, # schedule_to_close_timeout=timedelta( - seconds=30 if debug or testing else 600 + seconds=30 + if debug or testing + else temporal_schedule_to_close_timeout ), retry_policy=DEFAULT_RETRY_POLICY, ) @@ -218,6 +197,18 @@ async def run( # 3. Then, based on the outcome and step type, decide what to do next workflow.logger.info(f"Processing outcome for step {context.cursor.step}") + [outcome] = await workflow.execute_activity( + load_inputs_remote, + args=[[outcome]], + schedule_to_close_timeout=timedelta( + seconds=60 if debug or testing else temporal_schedule_to_close_timeout + ), + retry_policy=DEFAULT_RETRY_POLICY, + ) + + # Init state + state = None + match context.current_step, outcome: # Handle errors (activity returns None) case step, StepOutcome(error=error) if error is not None: @@ -258,7 +249,6 @@ async def run( switch=switch, index=index, previous_inputs=previous_inputs, - user_state=self.user_state, ) state = PartialTransition(output=result) @@ -276,7 +266,6 @@ async def run( else_branch=else_branch, condition=condition, previous_inputs=previous_inputs, - user_state=self.user_state, ) state = PartialTransition(output=result) @@ -288,7 +277,6 @@ async def run( do_step=do_step, items=items, previous_inputs=previous_inputs, - user_state=self.user_state, ) state = PartialTransition(output=result) @@ -303,7 +291,6 @@ async def run( reduce=reduce, initial=initial, previous_inputs=previous_inputs, - user_state=self.user_state, ) state = PartialTransition(output=result) @@ -316,7 +303,6 @@ async def run( map_defn=map_defn, items=items, previous_inputs=previous_inputs, - user_state=self.user_state, initial=initial, reduce=reduce, parallelism=parallelism, @@ -376,7 +362,6 @@ async def run( context, start=yield_next_target, previous_inputs=[output], - user_state=self.user_state, ) state = PartialTransition(output=result) @@ -393,11 +378,11 @@ async def run( state = PartialTransition(type="resume", output=result) - case PromptStep(unwrap=True), StepOutcome(output=response): - workflow.logger.debug(f"Prompt step: Received response: {response}") - state = PartialTransition(output=response) + case PromptStep(unwrap=True), StepOutcome(output=message): + workflow.logger.debug(f"Prompt step: Received response: {message}") + state = PartialTransition(output=message) - case PromptStep(forward_tool_results=False, unwrap=False), StepOutcome( + case PromptStep(auto_run_tools=False, unwrap=False), StepOutcome( output=response ): workflow.logger.debug(f"Prompt step: Received response: {response}") @@ -409,12 +394,22 @@ async def run( workflow.logger.debug(f"Prompt step: Received response: {response}") state = PartialTransition(output=response) - case PromptStep(unwrap=False), StepOutcome(output=response) if response[ - "choices" - ][0]["finish_reason"] == "tool_calls": - workflow.logger.debug("Prompt step: Received tool call") - message = response["choices"][0]["message"] - tool_calls_input = message["tool_calls"] + ## TODO: Handle multiple tool calls and multiple choices + # case PromptStep(unwrap=False), StepOutcome(output=response) if response[ + # "choices" + # ][0]["finish_reason"] == "tool_calls": + # workflow.logger.debug("Prompt step: Received tool call") + # message = response["choices"][0]["message"] + # tool_calls_input = message["tool_calls"] + + case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome( + output=response + ) if (choice := response["choices"][0])[ + "finish_reason" + ] == "tool_calls" and (tool_calls_input := choice["message"]["tool_calls"])[ + 0 + ]["type"] not in ["integration", "api_call", "system"]: + workflow.logger.debug("Prompt step: Received FUNCTION tool call") # Enter a wait-for-input step to ask the developer to run the tool calls tool_calls_results = await workflow.execute_activity( @@ -431,22 +426,80 @@ async def run( task_steps.prompt_step, context, schedule_to_close_timeout=timedelta( - seconds=30 if debug or testing else 600 + seconds=30 + if debug or testing + else temporal_schedule_to_close_timeout ), retry_policy=DEFAULT_RETRY_POLICY, ) state = PartialTransition(output=new_response.output, type="resume") + case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome( + output=response + ) if (choice := response["choices"][0])[ + "finish_reason" + ] == "tool_calls" and (tool_calls_input := choice["message"]["tool_calls"])[ + 0 + ]["type"] == "integration": + workflow.logger.debug("Prompt step: Received INTEGRATION tool call") + + # FIXME: Implement integration tool calls + # See: MANUAL TOOL CALL INTEGRATION (below) + raise NotImplementedError("Integration tool calls not yet supported") + + # TODO: Feed the tool call results back to the model (see above) + + case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome( + output=response + ) if (choice := response["choices"][0])[ + "finish_reason" + ] == "tool_calls" and (tool_calls_input := choice["message"]["tool_calls"])[ + 0 + ]["type"] == "api_call": + workflow.logger.debug("Prompt step: Received API_CALL tool call") + + # FIXME: Implement API_CALL tool calls + # See: MANUAL TOOL CALL API_CALL (below) + raise NotImplementedError("API_CALL tool calls not yet supported") + + # TODO: Feed the tool call results back to the model (see above) + + case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome( + output=response + ) if (choice := response["choices"][0])[ + "finish_reason" + ] == "tool_calls" and (tool_calls_input := choice["message"]["tool_calls"])[ + 0 + ]["type"] == "system": + workflow.logger.debug("Prompt step: Received SYSTEM tool call") + + # FIXME: Implement SYSTEM tool calls + # See: MANUAL TOOL CALL SYSTEM (below) + raise NotImplementedError("SYSTEM tool calls not yet supported") + + # TODO: Feed the tool call results back to the model (see above) + + case PromptStep(unwrap=False), StepOutcome(output=response) if ( + choice := response["choices"][0] + )["finish_reason"] == "tool_calls" and ( + tool_calls_input := choice["message"]["tool_calls"] + )[0]["type"] not in ["function", "integration", "api_call", "system"]: + workflow.logger.debug( + f"Prompt step: Received unknown tool call: {tool_calls_input[0]['type']}" + ) + state = PartialTransition(output=response) + case SetStep(), StepOutcome(output=evaluated_output): workflow.logger.info("Set step: Updating user state") - self.update_user_state(evaluated_output) # Pass along the previous output unchanged - state = PartialTransition(output=context.current_input) + state = PartialTransition( + output=context.current_input, user_state=evaluated_output + ) case GetStep(get=key), _: workflow.logger.info(f"Get step: Fetching '{key}' from user state") - value = self.get_user_state_by_key(key) + value = workflow.memo_value(key, default=None) workflow.logger.debug(f"Retrieved value: {value}") state = PartialTransition(output=value) @@ -473,20 +526,29 @@ async def run( case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[ "type" ] == "integration": + # MANUAL TOOL CALL INTEGRATION + workflow.logger.debug("ToolCallStep: Received INTEGRATION tool call") call = tool_call["integration"] tool_name = call["name"] arguments = call["arguments"] - integration_spec = next( + integration_tool = next( (t for t in context.tools if t.name == tool_name), None ) - if integration_spec is None: + if integration_tool is None: raise ApplicationError(f"Integration {tool_name} not found") - integration = IntegrationDef( - provider=integration_spec.spec["provider"], - setup=integration_spec.spec["setup"], - method=integration_spec.spec["method"], + provider = integration_tool.integration.provider + setup = ( + integration_tool.integration.setup + and integration_tool.integration.setup.model_dump() + ) + method = integration_tool.integration.method + + integration = BaseIntegrationDef( + provider=provider, + setup=setup, + method=method, arguments=arguments, ) @@ -494,7 +556,9 @@ async def run( execute_integration, args=[context, tool_name, integration, arguments], schedule_to_close_timeout=timedelta( - seconds=30 if debug or testing else 600 + seconds=30 + if debug or testing + else temporal_schedule_to_close_timeout ), retry_policy=DEFAULT_RETRY_POLICY, ) @@ -504,21 +568,23 @@ async def run( case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[ "type" ] == "api_call": + # MANUAL TOOL CALL API_CALL + workflow.logger.debug("ToolCallStep: Received API_CALL tool call") call = tool_call["api_call"] tool_name = call["name"] arguments = call["arguments"] - apicall_spec = next( + apicall_tool = next( (t for t in context.tools if t.name == tool_name), None ) - if apicall_spec is None: + if apicall_tool is None: raise ApplicationError(f"Integration {tool_name} not found") api_call = ApiCallDef( - method=apicall_spec.spec["method"], - url=apicall_spec.spec["url"], - headers=apicall_spec.spec["headers"], - follow_redirects=apicall_spec.spec["follow_redirects"], + method=apicall_tool.api_call.method, + url=apicall_tool.api_call.url, + headers=apicall_tool.api_call.headers, + follow_redirects=apicall_tool.api_call.follow_redirects, ) if "json_" in arguments: @@ -533,7 +599,9 @@ async def run( arguments, ], schedule_to_close_timeout=timedelta( - seconds=30 if debug or testing else 600 + seconds=30 + if debug or testing + else temporal_schedule_to_close_timeout ), ) @@ -542,6 +610,8 @@ async def run( case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[ "type" ] == "system": + # MANUAL TOOL CALL SYSTEM + workflow.logger.debug("ToolCallStep: Received SYSTEM tool call") call = tool_call.get("system") system_call = SystemDef(**call) @@ -549,29 +619,28 @@ async def run( execute_system, args=[context, system_call], schedule_to_close_timeout=timedelta( - seconds=30 if debug or testing else 600 + seconds=30 + if debug or testing + else temporal_schedule_to_close_timeout ), ) - # FIXME: This is a hack to make the output of the system call match - # the expected output format (convert uuid/datetime to strings) - def model_dump(obj): - if isinstance(obj, list): - return [model_dump(item) for item in obj] - return obj.model_dump(mode="json") - - state = PartialTransition(output=model_dump(tool_call_response)) + state = PartialTransition(output=tool_call_response) case _: workflow.logger.error( f"Unhandled step type: {type(context.current_step).__name__}" ) + state = PartialTransition(type="error", output="Not implemented") + await transition(context, state) + raise ApplicationError("Not implemented") # 4. Transition to the next step workflow.logger.info(f"Transitioning after step {context.cursor.step}") # The returned value is the transition finally created + state = state or PartialTransition(type="error", output="Not implemented") final_state = await transition(context, state) # --- @@ -591,10 +660,22 @@ def model_dump(obj): f"Continuing to next step: {final_state.next.workflow}.{final_state.next.step}" ) + # Save the final output to the blob store + [final_output] = await workflow.execute_activity( + save_inputs_remote, + args=[[final_state.output]], + schedule_to_close_timeout=timedelta( + seconds=10 if debug or testing else temporal_schedule_to_close_timeout + ), + retry_policy=DEFAULT_RETRY_POLICY, + ) + + previous_inputs.append(final_output) + # Continue as a child workflow return await continue_as_child( context.execution_input, start=final_state.next, - previous_inputs=previous_inputs + [final_state.output], - user_state=self.user_state, + previous_inputs=previous_inputs, + user_state=state.user_state, ) diff --git a/agents-api/agents_api/workflows/task_execution/helpers.py b/agents-api/agents_api/workflows/task_execution/helpers.py index 04449db58..3bf8c3b83 100644 --- a/agents-api/agents_api/workflows/task_execution/helpers.py +++ b/agents-api/agents_api/workflows/task_execution/helpers.py @@ -10,6 +10,7 @@ with workflow.unsafe.imports_passed_through(): from ...activities import task_steps from ...autogen.openapi_model import ( + EvaluateStep, TransitionTarget, Workflow, WorkflowStep, @@ -18,27 +19,38 @@ ExecutionInput, StepContext, ) + from ...common.storage_handler import auto_blob_store_workflow from ...env import task_max_parallelism +@auto_blob_store_workflow async def continue_as_child( execution_input: ExecutionInput, start: TransitionTarget, previous_inputs: list[Any], user_state: dict[str, Any] = {}, ) -> Any: - return await workflow.execute_child_workflow( - "TaskExecutionWorkflow", + info = workflow.info() + + if info.is_continue_as_new_suggested(): + run = workflow.continue_as_new + else: + run = lambda *args, **kwargs: workflow.execute_child_workflow( # noqa: E731 + info.workflow_type, *args, **kwargs + ) + + return await run( args=[ execution_input, start, previous_inputs, - user_state, ], retry_policy=DEFAULT_RETRY_POLICY, + memo=workflow.memo() | user_state, ) +@auto_blob_store_workflow async def execute_switch_branch( *, context: StepContext, @@ -46,7 +58,7 @@ async def execute_switch_branch( switch: list, index: int, previous_inputs: list[Any], - user_state: dict[str, Any], + user_state: dict[str, Any] = {}, ) -> Any: workflow.logger.info(f"Switch step: Chose branch {index}") chosen_branch = switch[index] @@ -54,7 +66,10 @@ async def execute_switch_branch( case_wf_name = f"`{context.cursor.workflow}`[{context.cursor.step}].case" case_task = execution_input.task.model_copy() - case_task.workflows = [Workflow(name=case_wf_name, steps=[chosen_branch.then])] + case_task.workflows = [ + Workflow(name=case_wf_name, steps=[chosen_branch.then]), + *case_task.workflows, + ] case_execution_input = execution_input.model_copy() case_execution_input.task = case_task @@ -69,24 +84,31 @@ async def execute_switch_branch( ) +@auto_blob_store_workflow async def execute_if_else_branch( *, context: StepContext, execution_input: ExecutionInput, then_branch: WorkflowStep, - else_branch: WorkflowStep, + else_branch: WorkflowStep | None, condition: bool, previous_inputs: list[Any], - user_state: dict[str, Any], + user_state: dict[str, Any] = {}, ) -> Any: workflow.logger.info(f"If-Else step: Condition evaluated to {condition}") chosen_branch = then_branch if condition else else_branch + if chosen_branch is None: + chosen_branch = EvaluateStep(evaluate={"output": "_"}) + if_else_wf_name = f"`{context.cursor.workflow}`[{context.cursor.step}].if_else" if_else_wf_name += ".then" if condition else ".else" if_else_task = execution_input.task.model_copy() - if_else_task.workflows = [Workflow(name=if_else_wf_name, steps=[chosen_branch])] + if_else_task.workflows = [ + Workflow(name=if_else_wf_name, steps=[chosen_branch]), + *if_else_task.workflows, + ] if_else_execution_input = execution_input.model_copy() if_else_execution_input.task = if_else_task @@ -101,6 +123,7 @@ async def execute_if_else_branch( ) +@auto_blob_store_workflow async def execute_foreach_step( *, context: StepContext, @@ -108,7 +131,7 @@ async def execute_foreach_step( do_step: WorkflowStep, items: list[Any], previous_inputs: list[Any], - user_state: dict[str, Any], + user_state: dict[str, Any] = {}, ) -> Any: workflow.logger.info(f"Foreach step: Iterating over {len(items)} items") results = [] @@ -118,7 +141,10 @@ async def execute_foreach_step( f"`{context.cursor.workflow}`[{context.cursor.step}].foreach[{i}]" ) foreach_task = execution_input.task.model_copy() - foreach_task.workflows = [Workflow(name=foreach_wf_name, steps=[do_step])] + foreach_task.workflows = [ + Workflow(name=foreach_wf_name, steps=[do_step]), + *foreach_task.workflows, + ] foreach_execution_input = execution_input.model_copy() foreach_execution_input.task = foreach_task @@ -135,6 +161,7 @@ async def execute_foreach_step( return results +@auto_blob_store_workflow async def execute_map_reduce_step( *, context: StepContext, @@ -142,7 +169,7 @@ async def execute_map_reduce_step( map_defn: WorkflowStep, items: list[Any], previous_inputs: list[Any], - user_state: dict[str, Any], + user_state: dict[str, Any] = {}, reduce: str | None = None, initial: Any = [], ) -> Any: @@ -155,7 +182,10 @@ async def execute_map_reduce_step( f"`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}]" ) map_reduce_task = execution_input.task.model_copy() - map_reduce_task.workflows = [Workflow(name=workflow_name, steps=[map_defn])] + map_reduce_task.workflows = [ + Workflow(name=workflow_name, steps=[map_defn]), + *map_reduce_task.workflows, + ] map_reduce_execution_input = execution_input.model_copy() map_reduce_execution_input.task = map_reduce_task @@ -178,6 +208,7 @@ async def execute_map_reduce_step( return result +@auto_blob_store_workflow async def execute_map_reduce_step_parallel( *, context: StepContext, @@ -185,7 +216,7 @@ async def execute_map_reduce_step_parallel( map_defn: WorkflowStep, items: list[Any], previous_inputs: list[Any], - user_state: dict[str, Any], + user_state: dict[str, Any] = {}, initial: Any = [], reduce: str | None = None, parallelism: int = task_max_parallelism, @@ -218,7 +249,10 @@ async def execute_map_reduce_step_parallel( # Note: Added PAR: prefix to easily identify parallel batches in logs workflow_name = f"PAR:`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}][{j}]" map_reduce_task = execution_input.task.model_copy() - map_reduce_task.workflows = [Workflow(name=workflow_name, steps=[map_defn])] + map_reduce_task.workflows = [ + Workflow(name=workflow_name, steps=[map_defn]), + *map_reduce_task.workflows, + ] map_reduce_execution_input = execution_input.model_copy() map_reduce_execution_input.task = map_reduce_task diff --git a/agents-api/agents_api/workflows/task_execution/transition.py b/agents-api/agents_api/workflows/task_execution/transition.py index 035322dad..1bc7a7056 100644 --- a/agents-api/agents_api/workflows/task_execution/transition.py +++ b/agents-api/agents_api/workflows/task_execution/transition.py @@ -12,6 +12,9 @@ from ...common.protocol.tasks import PartialTransition, StepContext from ...common.retry_policies import DEFAULT_RETRY_POLICY +with workflow.unsafe.imports_passed_through(): + from ...env import debug, temporal_schedule_to_close_timeout, testing + async def transition( context: StepContext, state: PartialTransition | None = None, **kwargs @@ -26,6 +29,7 @@ async def transition( state.type = "finish_branch" case _, _: state.type = "step" + transition_request = CreateTransitionRequest( current=context.cursor, **{ @@ -35,7 +39,8 @@ async def transition( workflow=context.cursor.workflow, step=context.cursor.step + 1 ), "metadata": {"step_type": type(context.current_step).__name__}, - **state.model_dump(exclude_unset=True), + "output": state.output, + **state.model_dump(exclude_unset=True, exclude={"output"}), **kwargs, # Override with any additional kwargs }, ) @@ -44,7 +49,9 @@ async def transition( return await workflow.execute_activity( task_steps.transition_step, args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=30), + schedule_to_close_timeout=timedelta( + seconds=30 if debug or testing else temporal_schedule_to_close_timeout + ), retry_policy=DEFAULT_RETRY_POLICY, ) diff --git a/agents-api/agents_api/workflows/truncation.py b/agents-api/agents_api/workflows/truncation.py index d12a186b9..a166a3d6d 100644 --- a/agents-api/agents_api/workflows/truncation.py +++ b/agents-api/agents_api/workflows/truncation.py @@ -8,6 +8,7 @@ with workflow.unsafe.imports_passed_through(): from ..activities.truncation import truncation from ..common.retry_policies import DEFAULT_RETRY_POLICY + from ..env import temporal_schedule_to_close_timeout @workflow.defn @@ -17,6 +18,8 @@ async def run(self, session_id: str, token_count_threshold: int) -> None: return await workflow.execute_activity( truncation, args=[session_id, token_count_threshold], - schedule_to_close_timeout=timedelta(seconds=600), + schedule_to_close_timeout=timedelta( + seconds=temporal_schedule_to_close_timeout + ), retry_policy=DEFAULT_RETRY_POLICY, ) diff --git a/agents-api/docker-compose.yml b/agents-api/docker-compose.yml index e086affbf..9f083a85d 100644 --- a/agents-api/docker-compose.yml +++ b/agents-api/docker-compose.yml @@ -20,11 +20,21 @@ x--shared-environment: &shared-environment TEMPORAL_NAMESPACE: ${TEMPORAL_NAMESPACE:-default} TEMPORAL_TASK_QUEUE: ${TEMPORAL_TASK_QUEUE:-julep-task-queue} TEMPORAL_WORKER_URL: ${TEMPORAL_WORKER_URL:-temporal:7233} + TEMPORAL_SCHEDULE_TO_CLOSE_TIMEOUT: ${TEMPORAL_SCHEDULE_TO_CLOSE_TIMEOUT:-3600} TRUNCATE_EMBED_TEXT: ${TRUNCATE_EMBED_TEXT:-True} WORKER_URL: ${WORKER_URL:-temporal:7233} + USE_BLOB_STORE_FOR_TEMPORAL: ${USE_BLOB_STORE_FOR_TEMPORAL:-false} + BLOB_STORE_CUTOFF_KB: ${BLOB_STORE_CUTOFF_KB:-128} + BLOB_STORE_BUCKET: ${BLOB_STORE_BUCKET:-agents-api} + S3_ENDPOINT: ${S3_ENDPOINT:-http://seaweedfs:8333} + S3_ACCESS_KEY: ${S3_ACCESS_KEY} + S3_SECRET_KEY: ${S3_SECRET_KEY} + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + MAX_FREE_SESSIONS: ${MAX_FREE_SESSIONS:-50} + MAX_FREE_EXECUTIONS: ${MAX_FREE_EXECUTIONS:-50} x--base-agents-api: &base-agents-api - image: julepai/agents-api:${TAG} + image: julepai/agents-api:${TAG:-dev} depends_on: worker: condition: service_started @@ -34,6 +44,8 @@ x--base-agents-api: &base-agents-api context: . dockerfile: Dockerfile + restart: on-failure + ports: - "8080:8080" @@ -44,6 +56,11 @@ x--base-agents-api: &base-agents-api target: /app/agents_api ignore: - ./**/*.pyc + - action: sync+restart + path: ./gunicorn_conf.py + target: /app/gunicorn_conf.py + ignore: + - ./gunicorn_conf.pyc - action: rebuild path: poetry.lock - action: rebuild @@ -67,7 +84,7 @@ services: AGENTS_API_PREFIX: "/api" worker: - image: julepai/worker:${TAG} + image: julepai/worker:${TAG:-dev} environment: <<: *shared-environment build: @@ -87,7 +104,7 @@ services: path: Dockerfile.worker cozo-migrate: - image: julepai/cozo-migrate:${TAG} + image: julepai/cozo-migrate:${TAG:-dev} container_name: cozo-migrate build: context: . diff --git a/agents-api/gunicorn_conf.py b/agents-api/gunicorn_conf.py new file mode 100644 index 000000000..43bd05e47 --- /dev/null +++ b/agents-api/gunicorn_conf.py @@ -0,0 +1,13 @@ +import multiprocessing +import os + +debug = os.getenv("AGENTS_API_DEBUG", "false").lower() == "true" + +# Gunicorn config variables +workers = (multiprocessing.cpu_count() - 1) if not debug else 1 +worker_class = "uvicorn.workers.UvicornWorker" +bind = "0.0.0.0:8080" +keepalive = 120 +timeout = 120 +errorlog = "-" +accesslog = "-" diff --git a/agents-api/migrations/migrate_1729114011_tweak_proximity_indices.py b/agents-api/migrations/migrate_1729114011_tweak_proximity_indices.py new file mode 100644 index 000000000..4852f3603 --- /dev/null +++ b/agents-api/migrations/migrate_1729114011_tweak_proximity_indices.py @@ -0,0 +1,133 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "tweak_proximity_indices" +CREATED_AT = 1729114011.022733 + + +def run(client, *queries): + joiner = "}\n\n{" + + query = joiner.join(queries) + query = f"{{\n{query}\n}}" + client.run(query) + + +# See: https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md +drop_snippets_hnsw_index = dict( + down=""" + ::hnsw create snippets:embedding_space { + fields: [embedding], + filter: !is_null(embedding), + dim: 1024, + distance: Cosine, + m: 64, + ef_construction: 256, + extend_candidates: true, + keep_pruned_connections: false, + } + """, + up=""" + ::hnsw drop snippets:embedding_space + """, +) + + +# See: https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md +snippets_hnsw_index = dict( + up=""" + ::hnsw create snippets:embedding_space { + fields: [embedding], + filter: !is_null(embedding), + dim: 1024, + distance: Cosine, + m: 64, + ef_construction: 800, + extend_candidates: false, + keep_pruned_connections: false, + } + """, + down=""" + ::hnsw drop snippets:embedding_space + """, +) + +drop_snippets_lsh_index = dict( + up=""" + ::lsh drop snippets:lsh + """, + down=""" + ::lsh create snippets:lsh { + extractor: content, + tokenizer: Simple, + filters: [Stopwords('en')], + n_perm: 200, + target_threshold: 0.9, + n_gram: 3, + false_positive_weight: 1.0, + false_negative_weight: 1.0, + } + """, +) + +snippets_lsh_index = dict( + up=""" + ::lsh create snippets:lsh { + extractor: content, + tokenizer: Simple, + filters: [Lowercase, AsciiFolding, Stemmer('english'), Stopwords('en')], + n_perm: 200, + target_threshold: 0.5, + n_gram: 2, + false_positive_weight: 1.0, + false_negative_weight: 1.0, + } + """, + down=""" + ::lsh drop snippets:lsh + """, +) + +# See: https://docs.cozodb.org/en/latest/vector.html#full-text-search-fts +drop_snippets_fts_index = dict( + down=""" + ::fts create snippets:fts { + extractor: content, + tokenizer: Simple, + filters: [Lowercase, Stemmer('english'), Stopwords('en')], + } + """, + up=""" + ::fts drop snippets:fts + """, +) + +# See: https://docs.cozodb.org/en/latest/vector.html#full-text-search-fts +snippets_fts_index = dict( + up=""" + ::fts create snippets:fts { + extractor: content, + tokenizer: Simple, + filters: [Lowercase, AsciiFolding, Stemmer('english'), Stopwords('en')], + } + """, + down=""" + ::fts drop snippets:fts + """, +) + +queries_to_run = [ + drop_snippets_hnsw_index, + drop_snippets_lsh_index, + drop_snippets_fts_index, + snippets_hnsw_index, + snippets_lsh_index, + snippets_fts_index, +] + + +def up(client): + run(client, *[q["up"] for q in queries_to_run]) + + +def down(client): + run(client, *[q["down"] for q in reversed(queries_to_run)]) diff --git a/agents-api/migrations/migrate_1731143165_support_tool_call_id.py b/agents-api/migrations/migrate_1731143165_support_tool_call_id.py new file mode 100644 index 000000000..9faf4d577 --- /dev/null +++ b/agents-api/migrations/migrate_1731143165_support_tool_call_id.py @@ -0,0 +1,100 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "support_tool_call_id" +CREATED_AT = 1731143165.95882 + +update_entries = { + "down": """ + ?[ + session_id, + entry_id, + source, + role, + name, + content, + token_count, + tokenizer, + created_at, + timestamp, + ] := *entries{ + session_id, + entry_id, + source, + role, + name, + content: content_string, + token_count, + tokenizer, + created_at, + timestamp, + }, content = [{"type": "text", "content": content_string}] + + :replace entries { + session_id: Uuid, + entry_id: Uuid default random_uuid_v4(), + source: String, + role: String, + name: String? default null, + => + content: [Json], + token_count: Int, + tokenizer: String, + created_at: Float default now(), + timestamp: Float default now(), + } + """, + "up": """ + ?[ + session_id, + entry_id, + source, + role, + name, + content, + token_count, + tokenizer, + created_at, + timestamp, + tool_call_id, + tool_calls, + ] := *entries{ + session_id, + entry_id, + source, + role, + name, + content: content_string, + token_count, + tokenizer, + created_at, + timestamp, + }, + content = [{"type": "text", "content": content_string}], + tool_call_id = null, + tool_calls = null + + :replace entries { + session_id: Uuid, + entry_id: Uuid default random_uuid_v4(), + source: String, + role: String, + name: String? default null, + => + content: [Json], + tool_call_id: String? default null, + tool_calls: [Json]? default null, + token_count: Int, + tokenizer: String, + created_at: Float default now(), + timestamp: Float default now(), + } + """, +} + + +def up(client): + client.run(update_entries["up"]) + + +def down(client): + client.run(update_entries["down"]) diff --git a/agents-api/migrations/migrate_1731953383_create_files_relation.py b/agents-api/migrations/migrate_1731953383_create_files_relation.py new file mode 100644 index 000000000..9cdc4f8fe --- /dev/null +++ b/agents-api/migrations/migrate_1731953383_create_files_relation.py @@ -0,0 +1,29 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "create_files_relation" +CREATED_AT = 1731953383.258172 + +create_files_query = dict( + up=""" + :create files { + developer_id: Uuid, + file_id: Uuid, + => + name: String, + description: String default "", + mime_type: String? default null, + size: Int, + hash: String, + created_at: Float default now(), + } + """, + down="::remove files", +) + + +def up(client): + client.run(create_files_query["up"]) + + +def down(client): + client.run(create_files_query["down"]) diff --git a/agents-api/notebooks/01-revise-entities.ipynb b/agents-api/notebooks/01-revise-entities.ipynb index 00667cb82..27b2e1f0d 100644 --- a/agents-api/notebooks/01-revise-entities.ipynb +++ b/agents-api/notebooks/01-revise-entities.ipynb @@ -2,16 +2,64 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: openai in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.51.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.6.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.27.2)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.5.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (2.9.2)\n", + "Requirement already satisfied: sniffio in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.66.5)\n", + "Requirement already satisfied: typing-extensions<5,>=4.11 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.12.2)\n", + "Requirement already satisfied: idna>=2.8 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.10)\n", + "Requirement already satisfied: certifi in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.6)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.23.4)\n" + ] + } + ], + "source": [ + "! pip install openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: python-dotenv in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.0.1)\n" + ] + } + ], + "source": [ + "! pip install python-dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "True" + "False" ] }, - "execution_count": 6, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -35,18 +83,22 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "from openai import Client\n", + "import openai\n", + "from openai import OpenAI\n", + "\n", + "api_key = \"YOUR_OPENAI_API_KEY\"\n", "\n", - "client = Client()" + "openai.api_key = api_key\n", + "client = OpenAI(api_key=api_key)" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -69,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -89,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -106,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -290,7 +342,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -314,7 +366,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -323,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -339,7 +391,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -348,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -365,7 +417,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -381,7 +433,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -460,7 +512,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -468,44 +520,37 @@ "output_type": "stream", "text": [ "Starting CoT generation\n", - "{'content': 'Planning step by step:\\n'\n", + "{'content': 'To add context for future entries, we should outline the main '\n", + " 'entities in the session. These entities are the main people, '\n", + " 'places, or things that are most relevant to the conversation.\\n'\n", " '\\n'\n", - " '1. Identify key entities such as characters, games, and '\n", - " 'technology mentioned in the conversation.\\n'\n", - " '2. Provide context for each entity, explaining its relevance to '\n", - " 'the conversation and any additional useful information.\\n'\n", - " '3. Ensure the entities and their descriptions encapsulate the '\n", - " 'essence of the conversation for future reference or follow-up '\n", - " 'discussions.\\n'\n", - " '\\n'\n", - " 'Entities will be identified and described based on the '\n", - " \"conversation's content and flow. This will include the games \"\n", - " 'discussed, characters within those games, and any technology '\n", - " 'issues mentioned.',\n", + " 'Entities:\\n'\n", + " '1. User (The participant initiating the conversation, interested '\n", + " 'in video games and experiencing technical issues).\\n'\n", + " '2. Assistant (Engages in conversation about video games and '\n", + " 'offers technical advice).\\n'\n", + " '3. Red Dead Redemption 2 (Video game discussed, specifically the '\n", + " '\"Blood Feuds, Ancient and Modern\" mission and the character '\n", + " 'development of Arthur and Dutch).\\n'\n", + " '4. Helldivers 2 (Another video game discussed, focusing on '\n", + " 'gameplay, strategy, and specific in-game items like the laser '\n", + " 'cannon and guard dog).\\n'\n", + " '5. Nvidia (Referenced in relation to driver issues, particularly '\n", + " 'in the context of compatibility with Linux operating systems).',\n", " 'role': 'assistant'}\n", "End CoT generation\n", "Starting chatml generation\n", "End chatml generation\n", - "{'content': '- **User**: Engages in discussions about video games and faces '\n", + "{'content': '- User: Engages in discussions about video games and experiences '\n", " 'technical issues with Nvidia drivers on Linux.\\n'\n", - " '- **Assistant**: Provides conversation on video games, offers '\n", - " 'suggestions on game strategies, and gives technical advice '\n", - " 'regarding Nvidia drivers.\\n'\n", - " '- **Red Dead Redemption 2 (RDR2)**: A video game discussed '\n", - " 'extensively, particularly its missions and character '\n", - " 'development.\\n'\n", - " '- **Arthur Morgan**: A central character in RDR2, noted for his '\n", - " 'moral complexity and development throughout the game.\\n'\n", - " '- **Dutch van der Linde**: Another key character from RDR2, whose '\n", - " \"increasingly erratic decisions impact the game's storyline.\\n\"\n", - " '- **Helldivers 2**: A cooperative multiplayer game mentioned by '\n", - " 'the user, known for its intense gameplay and strategic team '\n", - " 'dynamics.\\n'\n", - " '- **Nvidia**: Technology company referenced in relation to driver '\n", - " 'compatibility issues with Linux operating systems.\\n'\n", - " '- **Linux**: Operating system mentioned as having compatibility '\n", - " \"issues with Nvidia drivers, affecting the user's gaming \"\n", - " 'experience.',\n", + " '- Assistant: Provides insights and engages in discussions about '\n", + " 'video games, offers technical advice.\\n'\n", + " '- Red Dead Redemption 2: A video game discussed for its missions '\n", + " 'and character development.\\n'\n", + " '- Helldivers 2: Another video game mentioned, focusing on '\n", + " 'gameplay and strategies.\\n'\n", + " '- Nvidia: Mentioned in relation to driver compatibility issues '\n", + " 'with Linux.',\n", " 'role': 'assistant'}\n" ] } @@ -513,11 +558,18 @@ "source": [ "pprint(get_entities(chat_session))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "julep", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -531,7 +583,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.12.1" } }, "nbformat": 4, diff --git a/agents-api/notebooks/02-trim-messages.ipynb b/agents-api/notebooks/02-trim-messages.ipynb index 2a5c58da2..bf03a76a0 100644 --- a/agents-api/notebooks/02-trim-messages.ipynb +++ b/agents-api/notebooks/02-trim-messages.ipynb @@ -4,14 +4,62 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: openai in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.51.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.6.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.27.2)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.5.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (2.9.2)\n", + "Requirement already satisfied: sniffio in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.66.5)\n", + "Requirement already satisfied: typing-extensions<5,>=4.11 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.12.2)\n", + "Requirement already satisfied: idna>=2.8 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.10)\n", + "Requirement already satisfied: certifi in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.6)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.23.4)\n" + ] + } + ], + "source": [ + "! pip install openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: python-dotenv in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.0.1)\n" + ] + } + ], + "source": [ + "! pip install python-dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "True" + "False" ] }, - "execution_count": 1, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -35,18 +83,22 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "from openai import Client\n", + "import openai\n", + "from openai import OpenAI\n", "\n", - "client = Client()" + "api_key = \"YOUR_OPENAI_API_KEY\"\n", + "\n", + "openai.api_key = api_key\n", + "client = OpenAI(api_key=api_key)" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -69,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -89,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -106,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -290,7 +342,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -351,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -360,7 +412,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -376,7 +428,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -385,7 +437,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -402,7 +454,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -418,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -493,7 +545,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -501,11 +553,11 @@ "output_type": "stream", "text": [ "Starting CoT generation\n", - "{'content': 'The messages are clear and concise, reflecting a casual '\n", - " 'conversation about gaming experiences and software issues. '\n", - " \"There's no need for trimming as each message contributes to the \"\n", - " 'flow of the conversation, maintaining user engagement and '\n", - " 'providing relevant information and responses.',\n", + "{'content': 'The conversation is generally concise and relevant to the casual, '\n", + " 'gaming-centered topic. Each message contributes to the dialogue '\n", + " 'by either providing information or prompting further discussion, '\n", + " 'and no excessive verbosity or extraneous details are present. '\n", + " 'Therefore, no trimming is required for this session.',\n", " 'role': 'assistant'}\n", "End CoT generation\n", "Starting chatml generation\n", @@ -701,11 +753,18 @@ "source": [ "pprint(trim_messages(chat_session))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "julep", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -719,7 +778,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.12.1" } }, "nbformat": 4, diff --git a/agents-api/notebooks/03-summarise.ipynb b/agents-api/notebooks/03-summarise.ipynb index 98e6f5e0a..7be4baf02 100644 --- a/agents-api/notebooks/03-summarise.ipynb +++ b/agents-api/notebooks/03-summarise.ipynb @@ -4,14 +4,62 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: openai in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.51.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.6.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.27.2)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.5.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (2.9.2)\n", + "Requirement already satisfied: sniffio in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.66.5)\n", + "Requirement already satisfied: typing-extensions<5,>=4.11 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.12.2)\n", + "Requirement already satisfied: idna>=2.8 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.10)\n", + "Requirement already satisfied: certifi in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.6)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.23.4)\n" + ] + } + ], + "source": [ + "! pip install openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: python-dotenv in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.0.1)\n" + ] + } + ], + "source": [ + "! pip install python-dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "True" + "False" ] }, - "execution_count": 1, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -42,18 +90,22 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "from openai import Client\n", + "import openai\n", + "from openai import OpenAI\n", + "\n", + "api_key = \"YOUR_OPENAI_API_KEY\"\n", "\n", - "client = Client()" + "openai.api_key = api_key\n", + "client = OpenAI(api_key=api_key)" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -80,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -100,7 +152,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -124,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -315,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -355,7 +407,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -429,7 +481,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -474,7 +526,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -490,7 +542,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -506,7 +558,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -515,7 +567,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -532,7 +584,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -555,7 +607,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -636,7 +688,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -644,41 +696,37 @@ "output_type": "stream", "text": [ "Starting CoT generation\n", - "{'content': 'To provide a comprehensive overview of the entities in the '\n", - " 'session, we need to focus on the main people, places, things, and '\n", - " 'concepts that have been discussed:\\n'\n", + "{'content': 'To summarize the important entities from this conversation:\\n'\n", " '\\n'\n", - " '1. **User**: Engages in conversation about video games, '\n", - " 'specifically mentioning experiences with \"Red Dead Redemption 2\" '\n", - " 'and \"Helldivers 2.\" Also, shares technical issues related to '\n", - " 'Nvidia graphics drivers on Linux.\\n'\n", + " '1. **User**: Engages in a casual and friendly chat with the '\n", + " 'Assistant, discussing video games and a technical issue.\\n'\n", + " '2. **Assistant**: Responds to the User, engaging in discussions '\n", + " 'about specific video games and offering technical advice '\n", + " 'regarding an Nvidia driver issue.\\n'\n", + " '3. **Video Games Discussed**:\\n'\n", + " ' - **Red Dead Redemption 2 (RDR2)**: User recently finished '\n", + " 'this game and discusses specific missions such as \"Blood Feuds, '\n", + " 'Ancient and Modern\".\\n'\n", + " ' - **Helldivers 2**: User is currently playing this game, '\n", + " 'mentioning favorite builds and discussing gameplay strategies.\\n'\n", + " '4. **Technical Issue**: User mentions having an Nvidia driver '\n", + " 'issue, particularly concerning its compatibility with Linux. The '\n", + " 'Assistant offers advice on handling this issue.\\n'\n", + " '5. **Game Characters**:\\n'\n", + " ' - **Arthur**: From RDR2, discussed in terms of his journey and '\n", + " 'character development.\\n'\n", + " ' - **Dutch**: Also from RDR2, discussed for his progressive '\n", + " 'craziness and leadership style.\\n'\n", + " '6. **Gaming Strategies**:\\n'\n", + " ' - **Run and Gun**: Mentioned by the User as a strategy used in '\n", + " 'Helldivers 2.\\n'\n", + " '7. **Operating System**:\\n'\n", + " ' - **Linux**: Mentioned in relation to the Nvidia driver '\n", + " 'compatibility issue.\\n'\n", " '\\n'\n", - " '2. **Assistant**: Offers insights and engages in discussions '\n", - " 'about the video games mentioned by the user, provides gameplay '\n", - " 'strategies, and offers technical advice regarding Nvidia driver '\n", - " 'issues on Linux.\\n'\n", - " '\\n'\n", - " '3. **Red Dead Redemption 2 (RDR2)**: A video game discussed '\n", - " 'extensively in the conversation. Key aspects such as the '\n", - " 'character Arthur, missions like \"Blood Feuds, Ancient and '\n", - " 'Modern,\" and side quests are highlighted.\\n'\n", - " '\\n'\n", - " '4. **Helldivers 2**: Another video game mentioned by the user. '\n", - " 'The conversation covers gameplay elements like the laser cannon, '\n", - " 'guard dog, and challenges like the Charger enemy.\\n'\n", - " '\\n'\n", - " '5. **Nvidia Drivers**: A technical issue brought up by the user, '\n", - " 'particularly focusing on compatibility problems with Linux. The '\n", - " 'assistant provides troubleshooting advice and recommendations for '\n", - " 'dealing with these issues.\\n'\n", - " '\\n'\n", - " '6. **Linux**: Mentioned in the context of having compatibility '\n", - " \"issues with Nvidia drivers, highlighting the user's struggle with \"\n", - " 'technical aspects of gaming on this operating system.\\n'\n", - " '\\n'\n", - " 'These entities encapsulate the core topics and issues discussed '\n", - " \"in the session, providing a clear view of the conversation's \"\n", - " 'focus areas.',\n", + " 'These entities encapsulate the main topics and references made '\n", + " 'during the conversation, focusing on video gaming experiences, '\n", + " 'character analysis, and technical troubleshooting.',\n", " 'role': 'assistant'}\n", "End CoT generation\n", "Starting chatml generation\n", @@ -699,7 +747,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -777,7 +825,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -785,19 +833,45 @@ "output_type": "stream", "text": [ "Starting CoT generation\n", - "{'content': 'The conversation between the user and the assistant revolves '\n", - " 'around video games and technical issues. The assistant engages '\n", - " 'enthusiastically about the user’s gaming experiences, providing '\n", - " 'tips and engaging on a technical level about Nvidia drivers on '\n", - " 'Linux towards the end. The user is generally responsive and '\n", - " 'shares specifics about their gaming preferences and technical '\n", - " 'difficulties. The tone is casual and friendly throughout, with '\n", - " 'the assistant providing support and showing interest in the '\n", - " \"user's activities. \\n\"\n", - " '\\n'\n", - " 'No trimming is required as the messages are well-paced and '\n", - " \"relevant to the users' interests, fostering an engaging and \"\n", - " 'informative dialogue.',\n", + "{'content': '\\n'\n", + " \"- User greeted the assistant and asked what's good.\\n\"\n", + " '- Assistant replied casually, asking about recent games.\\n'\n", + " '- User mentioned finishing Red Dead Redemption 2, praising the '\n", + " 'final mission.\\n'\n", + " \"- Assistant inquired about the user's thoughts on Arthur's \"\n", + " 'journey and favorite moments.\\n'\n", + " '- User highlighted the \"Blood Feuds\" mission.\\n'\n", + " \"- Assistant discussed the mission's cinematic aspects and asked \"\n", + " \"about user's alignment with game characters.\\n\"\n", + " '- User preferred Arthur over Dutch.\\n'\n", + " '- Assistant discussed character development and suggested '\n", + " 'exploring side quests.\\n'\n", + " '- User mentioned completing side quests, liked the widow and '\n", + " 'bounty missions.\\n'\n", + " \"- Assistant praised the game's side missions and asked about \"\n", + " \"user's next gaming plans.\\n\"\n", + " '- User switched to playing Helldivers 2.\\n'\n", + " \"- Assistant described Helldivers 2's gameplay and asked about \"\n", + " \"user's experience.\\n\"\n", + " '- User shared favorite equipment setup.\\n'\n", + " '- Assistant discussed tactical options and asked about '\n", + " 'challenging missions.\\n'\n", + " '- User mentioned difficulty with a specific enemy.\\n'\n", + " \"- Assistant offered strategy tips and inquired about team's \"\n", + " 'handling of game challenges.\\n'\n", + " '- User described their run-and-gun approach.\\n'\n", + " '- Assistant acknowledged the strategy and asked about difficulty '\n", + " 'levels.\\n'\n", + " '- User mentioned needing to work and a technical issue with '\n", + " 'Nvidia drivers.\\n'\n", + " '- Assistant offered technical advice for driver issue and wished '\n", + " 'good luck with work.\\n'\n", + " '- User commented on Nvidia and Linux compatibility issues.\\n'\n", + " '- Assistant provided detailed advice for handling Nvidia drivers '\n", + " 'on Linux.\\n'\n", + " '- User ended the conversation, intending to leave.\\n'\n", + " '- Assistant said goodbye.\\n'\n", + " '',\n", " 'role': 'assistant'}\n", "End CoT generation\n", "Starting chatml generation\n", @@ -811,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1011,7 +1085,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -1098,7 +1172,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1106,106 +1180,21 @@ "output_type": "stream", "text": [ "Starting CoT generation\n", - "{'content': '- Combine initial greetings and the start of the discussion about '\n", - " '\"Red Dead Redemption 2 (RDR2)\" into a summary.\\n'\n", - " '- Summarize the detailed conversation about specific missions and '\n", - " 'characters in \"RDR2\".\\n'\n", - " '- Retain individual entries about \"Helldivers 2\" due to the '\n", - " \"user's direct interaction and game preferences.\\n\"\n", - " '- Summarize discussions around \"Nvidia Drivers\" and \"Linux\" '\n", - " 'compatibility, emphasizing the solution offered.\\n'\n", - " '- Retain the closing conversation as it indicates the end of the '\n", - " 'session. \\n'\n", - " '\\n'\n", - " '\\n'\n", - " '[\\n'\n", - " ' {\\n'\n", - " ' \"role\": \"system\",\\n'\n", - " ' \"name\": \"summary\",\\n'\n", - " ' \"content\": \"Event: The user discussed finishing \\'Red Dead '\n", - " \"Redemption 2' and particularly enjoyed the last mission. They \"\n", - " 'also talked about favorite missions and characters, specifically '\n", - " 'Arthur over Dutch.\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 12,\\n'\n", - " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"im playing helldivers 2. do u know of it\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 13,\\n'\n", - " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"Yes, Helldivers 2! It\\'s a great choice if '\n", - " \"you're into intense co-op action. The game really ramps up with \"\n", - " 'its twin-stick shooter gameplay, and it\\\\u2019s super fun but '\n", - " 'also quite challenging. Working together with teammates to '\n", - " 'complete those missions can get pretty hectic. How are you '\n", - " 'finding the chaos and team dynamics so far?\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 14,\\n'\n", - " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"amazing. my fav build is laser cannon + guard '\n", - " 'dog. what about u\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 15,\\n'\n", - " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"Nice combo! The laser cannon is awesome for '\n", - " 'tearing through those enemy swarms, and the guard dog drone '\n", - " 'really helps in keeping the smaller threats at bay. I personally '\n", - " 'love using the railgun with a shield backpack. It gives a good '\n", - " 'balance of offense and defense, especially when things get really '\n", - " 'chaotic. What\\'s been the toughest mission for you so far?\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 16,\\n'\n", - " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"dude the charger is so difficult to kill.\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 17,\\n'\n", - " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"Yeah, the Charger can be brutal! They really test '\n", - " \"your reflexes and teamwork. It's all about timing and \"\n", - " 'coordination with your team to bring those heavy hitters down. '\n", - " 'Have you tried using more strategic traps or mines? Sometimes '\n", - " 'setting up the right position with static fields can really make '\n", - " 'a difference against them. How do your squadmates handle it?\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 18,\\n'\n", - " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"we just run and gun\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 19,\\n'\n", - " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"Classic strategy! That can definitely be fun, '\n", - " 'especially in a game as intense as Helldivers 2 where things get '\n", - " 'chaotic fast. Keeping on the move helps a ton. Just make sure '\n", - " \"someone's always got your back! Have you guys tried any of the \"\n", - " 'higher difficulty levels yet, or are you sticking to a certain '\n", - " 'comfort zone for now?\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"role\": \"system\",\\n'\n", - " ' \"name\": \"summary\",\\n'\n", - " ' \"content\": \"Event: The user discussed an Nvidia driver issue, '\n", - " 'particularly its incompatibility with Linux, and the assistant '\n", - " 'provided potential solutions and resources for assistance.\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 24,\\n'\n", - " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"yep. see u bye!\"\\n'\n", - " ' },\\n'\n", - " ' {\\n'\n", - " ' \"index\": 25,\\n'\n", - " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"See you, take care! Bye!\"\\n'\n", - " ' }\\n'\n", - " ']\\n',\n", + "{'content': '- Summarize the initial casual greeting between User and '\n", + " 'Assistant.\\n'\n", + " \"- Keep detailed discussion on RDR2, including User's favorite \"\n", + " 'moments and characters, as they directly relate to the entities '\n", + " 'outlined.\\n'\n", + " '- Summarize the transition from RDR2 discussion to Helldivers 2 '\n", + " 'without losing game-specific details.\\n'\n", + " '- Preserve details on Helldivers 2 gameplay strategies mentioned '\n", + " 'by User.\\n'\n", + " \"- Summarize User's mention of work and Nvidia driver issue while \"\n", + " 'retaining the essence of their tech problem.\\n'\n", + " \"- Retain Assistant's advice on Nvidia and Linux compatibility \"\n", + " \"issues, as it's relevant to the User's context.\\n\"\n", + " '- Keep the farewell message brief but intact to maintain the '\n", + " 'conversational closure.',\n", " 'role': 'assistant'}\n", "End CoT generation\n", "Starting chatml generation\n", @@ -1225,7 +1214,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -1236,88 +1225,180 @@ " ' {\\n'\n", " ' \"role\": \"system\",\\n'\n", " ' \"name\": \"summary\",\\n'\n", - " ' \"content\": \"Event: The user discussed finishing \\'Red Dead '\n", - " \"Redemption 2' and particularly enjoyed the last mission. They \"\n", - " 'also talked about favorite missions and characters, specifically '\n", - " 'Arthur over Dutch.\"\\n'\n", + " ' \"content\": \"Event: Casual greeting between User and '\n", + " 'Assistant, with User mentioning they recently finished playing '\n", + " 'Red Dead Redemption 2.\"\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"i just got done with finishing red dead '\n", + " 'redemption 2 recently. the last mission was so fkng coolll\",\\n'\n", + " ' \"index\": 4\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"Oh man, RDR2 is epic! That ending is intense, '\n", + " \"right? What did you think about Arthur's journey throughout the \"\n", + " 'game? Got any favorite moments?\",\\n'\n", + " ' \"index\": 5\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"blood feuds mission was amazing\",\\n'\n", + " ' \"index\": 6\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"Absolutely, the \\\\\"Blood Feuds, Ancient and '\n", + " 'Modern\\\\\" mission is a standout! The whole shootout at '\n", + " 'Braithwaite Manor is intense. The atmosphere Rockstar created '\n", + " 'during that mission with the burning building in the background '\n", + " 'is just cinematic perfection. Did you side more with Arthur or '\n", + " 'Dutch throughout the game?\",\\n'\n", + " ' \"index\": 7\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 12,\\n'\n", " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"im playing helldivers 2. do u know of it\"\\n'\n", + " ' \"content\": \"arthur of course. dutch just kept becoming '\n", + " 'crazier and crazier.\",\\n'\n", + " ' \"index\": 8\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"Totally get that. Arthur\\'s character development '\n", + " \"is really something else. He starts questioning Dutch's decisions \"\n", + " 'and looking at things more critically, which adds so much depth '\n", + " \"to his character. Watching Dutch's descent as the gang falls \"\n", + " 'apart is pretty intense. Have you thought about checking out any '\n", + " 'of the side quests or exploring more of the open world now that '\n", + " 'you\\'ve finished the main story?\",\\n'\n", + " ' \"index\": 9\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"oh i\\'ve done all the side quests. the widow '\n", + " 'missions are great but i also loved the bounty missions (where u '\n", + " 'have to talk to gangsters for an author)\",\\n'\n", + " ' \"index\": 10\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"Ah, the bounty missions for the author are a '\n", + " 'blast! Hunting down those gunslingers for the book gives you such '\n", + " \"a great tour of the game's diverse characters. And Charlotte's \"\n", + " \"missions, the widow, they're pretty touching, right? It\\\\u2019s \"\n", + " 'nice to see Arthur helping her adapt to wilderness life. Adds a '\n", + " 'softer side to the game amidst all the chaos.\\\\n\\\\nNow that '\n", + " 'you\\'ve conquered RDR2, any other games on your radar?\",\\n'\n", + " ' \"index\": 11\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"im playing helldivers 2. do u know of it\",\\n'\n", + " ' \"index\": 12\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 13,\\n'\n", " ' \"role\": \"assistant\",\\n'\n", " ' \"content\": \"Yes, Helldivers 2! It\\'s a great choice if '\n", " \"you're into intense co-op action. The game really ramps up with \"\n", " 'its twin-stick shooter gameplay, and it\\\\u2019s super fun but '\n", " 'also quite challenging. Working together with teammates to '\n", " 'complete those missions can get pretty hectic. How are you '\n", - " 'finding the chaos and team dynamics so far?\"\\n'\n", + " 'finding the chaos and team dynamics so far?\",\\n'\n", + " ' \"index\": 13\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 14,\\n'\n", " ' \"role\": \"user\",\\n'\n", " ' \"content\": \"amazing. my fav build is laser cannon + guard '\n", - " 'dog. what about u\"\\n'\n", + " 'dog. what about u\",\\n'\n", + " ' \"index\": 14\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 15,\\n'\n", " ' \"role\": \"assistant\",\\n'\n", " ' \"content\": \"Nice combo! The laser cannon is awesome for '\n", " 'tearing through those enemy swarms, and the guard dog drone '\n", " 'really helps in keeping the smaller threats at bay. I personally '\n", " 'love using the railgun with a shield backpack. It gives a good '\n", " 'balance of offense and defense, especially when things get really '\n", - " 'chaotic. What\\'s been the toughest mission for you so far?\"\\n'\n", + " 'chaotic. What\\'s been the toughest mission for you so far?\",\\n'\n", + " ' \"index\": 15\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 16,\\n'\n", " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"dude the charger is so difficult to kill.\"\\n'\n", + " ' \"content\": \"dude the charger is so difficult to kill.\",\\n'\n", + " ' \"index\": 16\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 17,\\n'\n", " ' \"role\": \"assistant\",\\n'\n", " ' \"content\": \"Yeah, the Charger can be brutal! They really test '\n", " \"your reflexes and teamwork. It's all about timing and \"\n", " 'coordination with your team to bring those heavy hitters down. '\n", " 'Have you tried using more strategic traps or mines? Sometimes '\n", " 'setting up the right position with static fields can really make '\n", - " 'a difference against them. How do your squadmates handle it?\"\\n'\n", + " 'a difference against them. How do your squadmates handle it?\",\\n'\n", + " ' \"index\": 17\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 18,\\n'\n", " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"we just run and gun\"\\n'\n", + " ' \"content\": \"we just run and gun\",\\n'\n", + " ' \"index\": 18\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 19,\\n'\n", " ' \"role\": \"assistant\",\\n'\n", " ' \"content\": \"Classic strategy! That can definitely be fun, '\n", " 'especially in a game as intense as Helldivers 2 where things get '\n", " 'chaotic fast. Keeping on the move helps a ton. Just make sure '\n", " \"someone's always got your back! Have you guys tried any of the \"\n", " 'higher difficulty levels yet, or are you sticking to a certain '\n", - " 'comfort zone for now?\"\\n'\n", + " 'comfort zone for now?\",\\n'\n", + " ' \"index\": 19\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"role\": \"system\",\\n'\n", - " ' \"name\": \"summary\",\\n'\n", - " ' \"content\": \"Event: The user discussed an Nvidia driver issue, '\n", - " 'particularly its incompatibility with Linux, and the assistant '\n", - " 'provided potential solutions and resources for assistance.\"\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"i think i have to go work soon. also have this '\n", + " 'nvidia driver issue that\\'s bugging me\",\\n'\n", + " ' \"index\": 20\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"Ah, bummer about the driver issue \\\\u2014 it\\'s '\n", + " 'annoying when tech stuff gets in the way of gaming. Maybe try '\n", + " \"doing a clean install of the drivers if you haven't already? It \"\n", + " 'can sometimes clear up persistent issues.\\\\n\\\\nAlso, good luck '\n", + " \"with work! Hopefully, you'll get some time later to sort out the \"\n", + " 'driver thing and jump back into some gaming. Let me know how it '\n", + " \"goes, and if you need more gaming chats or tech tips, I'm \"\n", + " 'here!\",\\n'\n", + " ' \"index\": 21\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"user\",\\n'\n", + " ' \"content\": \"nvidia and linux don\\'t go well together\",\\n'\n", + " ' \"index\": 22\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"assistant\",\\n'\n", + " ' \"content\": \"That\\'s true, Nvidia drivers can be tricky to '\n", + " 'handle on Linux due to compatibility issues sometimes. You might '\n", + " 'want to check out the latest Nvidia driver from their official '\n", + " 'site or a stable version known to work well with your specific '\n", + " 'Linux distribution. Using proprietary drivers instead of Nouveau '\n", + " 'could also potentially solve a lot of issues. Additionally, '\n", + " 'forums and community support boards like those on Ubuntu, Arch, '\n", + " 'or even broader communities like Reddit\\\\u2019s r/linux_gaming '\n", + " 'can be really helpful.\\\\n\\\\nHope that helps, and your driver woes '\n", + " 'get sorted soon so you can enjoy your gaming hassle-free!\",\\n'\n", + " ' \"index\": 23\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 24,\\n'\n", " ' \"role\": \"user\",\\n'\n", - " ' \"content\": \"yep. see u bye!\"\\n'\n", + " ' \"content\": \"yep. see u bye!\",\\n'\n", + " ' \"index\": 24\\n'\n", " ' },\\n'\n", " ' {\\n'\n", - " ' \"index\": 25,\\n'\n", " ' \"role\": \"assistant\",\\n'\n", - " ' \"content\": \"See you, take care! Bye!\"\\n'\n", + " ' \"content\": \"See you, take care! Bye!\",\\n'\n", + " ' \"index\": 25\\n'\n", " ' }\\n'\n", " ']',\n", " 'role': 'assistant'}\n" @@ -1327,11 +1408,18 @@ "source": [ "pprint(summarized_messages)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "julep", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1345,7 +1433,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.12.1" } }, "nbformat": 4, diff --git a/agents-api/notebooks/RecSum-experiments.ipynb b/agents-api/notebooks/RecSum-experiments.ipynb index 1315533dc..9060501b1 100644 --- a/agents-api/notebooks/RecSum-experiments.ipynb +++ b/agents-api/notebooks/RecSum-experiments.ipynb @@ -1,18 +1,68 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3127f8fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: openai in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.51.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.6.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.27.2)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (0.5.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (2.9.2)\n", + "Requirement already satisfied: sniffio in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.66.5)\n", + "Requirement already satisfied: typing-extensions<5,>=4.11 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from openai) (4.12.2)\n", + "Requirement already satisfied: idna>=2.8 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.10)\n", + "Requirement already satisfied: certifi in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.6)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.23.4)\n" + ] + } + ], + "source": [ + "! pip install openai" + ] + }, { "cell_type": "code", "execution_count": 2, + "id": "96efe2be", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: python-dotenv in /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages (1.0.1)\n" + ] + } + ], + "source": [ + "! pip install python-dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": 3, "id": "b03a4636-d57e-42e9-8a06-fdb7c6803708", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "True" + "False" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -23,24 +73,20 @@ "load_dotenv(\"../../.env\")" ] }, - { - "cell_type": "code", - "execution_count": 3, - "id": "eb80a352-ad21-423c-9284-32b21d271eba", - "metadata": {}, - "outputs": [], - "source": [ - "from openai import Client" - ] - }, { "cell_type": "code", "execution_count": 4, - "id": "4619f484-55f6-4122-8e26-27ec9e3506d5", + "id": "eb80a352-ad21-423c-9284-32b21d271eba", "metadata": {}, "outputs": [], "source": [ - "client = Client()" + "import openai\n", + "from openai import OpenAI\n", + "\n", + "api_key = \"YOUR_OPENAI_API_KEY\"\n", + "\n", + "openai.api_key = api_key\n", + "client = OpenAI(api_key=api_key)" ] }, { @@ -99,7 +145,7 @@ "\n", "\n", "def chat():\n", - " while (user_input := input(\"You: \")) != \"bye\":\n", + " while (user_input := input(\"You: \").lower()) != \"bye\":\n", " chat_session.append(user(user_input))\n", "\n", " result = generate(chat_session)\n", @@ -362,63 +408,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "You: hey\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GPT: Hey! How are you doing?\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You: good, how about you?\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GPT: I'm doing well, thank you for asking. Is there anything on your mind that you'd like to talk about or get some advice on?\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You: no why do you say that? did I sound troubled?\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GPT: Not at all! I just thought I'd check in and see if there's anything on your mind that you might need some advice or someone to talk to about. But if you're doing well, that's great to hear!\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You: no I didnt say I was ok just that I hadnt thought of asking for advice yet\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GPT: Ah, I see. Well, I'm here whenever you need a listening ear or some advice. Just let me know how I can help!\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You: bye\n" + "GPT: Hey there! I just wanted to remind you that it's important to take care of yourself both mentally and physically. Make sure to prioritize self-care and engage in activities that bring you joy and peace. Remember, it's okay to say no to things that don't align with your values or make you uncomfortable. Trust your instincts and surround yourself with positive influences. You deserve to be happy and fulfilled, so don't be afraid to pursue your dreams and make choices that are best for you. If you ever need a listening ear or some guidance, I'm always here for you!\n", + "GPT: I'm just a program, so I don't have feelings like humans do, but I'm here and ready to help you with anything you need. How can I support you today?\n" ] } ], @@ -442,7 +433,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 13, "id": "95431b29-73e2-4954-b6fc-1c8814a9249f", "metadata": {}, "outputs": [], @@ -452,7 +443,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "1eaabfe2-f399-428b-84d2-ed8c237b7d3d", "metadata": {}, "outputs": [], @@ -475,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "2a726b3c-493c-4df5-81a7-6b7b109c222e", "metadata": {}, "outputs": [], @@ -504,7 +495,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 16, "id": "6323b7b2-0aaf-4cea-896b-0c887054ce6e", "metadata": {}, "outputs": [], @@ -596,7 +587,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 17, "id": "0c2eaabc-76a1-412f-beb3-b64510e638a2", "metadata": {}, "outputs": [ @@ -604,57 +595,74 @@ "name": "stdout", "output_type": "stream", "text": [ - "1714643926.0711672\n", - "0.0017969608306884766\n", + "1728353832.57365\n", + "0.00043010711669921875\n", "Starting CoT generation\n", "{'content': 'Planning step by step:\\n'\n", - " '- Combine entries 1 and 2 into a summary of the greeting and '\n", - " 'initial chat.\\n'\n", - " '- Summarize the conversation about Red Dead Redemption 2 from '\n", - " 'entries 3 to 10, noting key points about the game and missions '\n", - " 'discussed.\\n'\n", - " '- Combine entries 11 and 12 into a summary about transitioning to '\n", - " 'the game Helldivers 2, including initial thoughts.\\n'\n", - " '- Summarize the specific discussion about Helldivers 2 gameplay '\n", - " 'and strategy from entries 13 to 18.\\n'\n", - " '- Summarize entries 19 and 20 discussing the user having to go to '\n", - " 'work and dealing with Nvidia driver issues.\\n'\n", - " '- Combine entries 21 and 22 to summarize the Nvidia and Linux '\n", - " 'driver compatibility discussion.\\n'\n", - " '- Combine entries 23 and 24 to a summary of the farewell.',\n", + " '- We can consolidate the initial greetings and mention of Red '\n", + " 'Dead Redemption 2 into a summary.\\n'\n", + " '- The detailed discussion about RDR2 and Helldivers 2 can be '\n", + " 'summarized while retaining key elements of the conversation.\\n'\n", + " \"- The user's brief mention of work and technical issues can be \"\n", + " 'summarized into one entry to capture the issue without losing '\n", + " 'context.\\n'\n", + " '- Farewells can be combined into a single entry.\\n'\n", + " '\\n'\n", + " \"Here's how we can compact the history: \\n\"\n", + " '\\n'\n", + " '1. Summarize the greeting and initial conversation about Red Dead '\n", + " 'Redemption 2.\\n'\n", + " '2. Summarize the detailed discussion on RDR2, focusing on the '\n", + " \"user's favorite moments and questions from the assistant.\\n\"\n", + " '3. Summarize the conversation about Helldivers 2, highlighting '\n", + " 'user preferences and challenges faced in gameplay.\\n'\n", + " \"4. Combine the entries about the user's Nvidia driver issue and \"\n", + " 'the farewell into two concise entries.',\n", " 'role': 'assistant'}\n", "End CoT generation\n", - "7.267635345458984\n", - "7.2676897048950195\n", + "6.3223512172698975\n", + "6.322365999221802\n", "Starting chatml generation\n", "End chatml generation\n", - "19.629928588867188\n", + "26.040874004364014\n", "{'content': '[\\n'\n", " ' {\\n'\n", " ' \"role\": \"system\",\\n'\n", - " ' \"content\": \"User greets the assistant and mentions finishing '\n", - " 'Red Dead Redemption 2, praising the final mission. The assistant '\n", - " \"responds enthusiastically, discussing the game's ending and key \"\n", - " 'missions.\"\\n'\n", + " ' \"name\": \"summary\",\\n'\n", + " ' \"content\": \"The user greeted the assistant and shared their '\n", + " 'recent completion of Red Dead Redemption 2, expressing excitement '\n", + " 'about the game\\'s final mission.\"\\n'\n", + " ' },\\n'\n", + " ' {\\n'\n", + " ' \"role\": \"system\",\\n'\n", + " ' \"name\": \"summary\",\\n'\n", + " ' \"content\": \"The user and the assistant discussed favorite '\n", + " \"moments from Red Dead Redemption 2, focusing on the 'Blood Feuds, \"\n", + " \"Ancient and Modern' mission and Arthur's character development. \"\n", + " 'The user expressed a preference for Arthur over Dutch and '\n", + " 'mentioned completing all side quests, including the widow and '\n", + " 'bounty missions.\"\\n'\n", " ' },\\n'\n", " ' {\\n'\n", " ' \"role\": \"system\",\\n'\n", - " ' \"content\": \"The conversation shifts to Helldivers 2, with the '\n", - " 'user sharing their favorite build and the assistant suggesting '\n", - " 'strategies. They discuss gameplay dynamics and the challenges of '\n", - " 'specific missions.\"\\n'\n", + " ' \"name\": \"summary\",\\n'\n", + " ' \"content\": \"The conversation shifted to Helldivers 2, where '\n", + " 'the user described their favorite build and gameplay strategy. '\n", + " 'They discussed the challenges of facing the Charger enemy and the '\n", + " 'run-and-gun approach they use with their squad.\"\\n'\n", " ' },\\n'\n", " ' {\\n'\n", " ' \"role\": \"system\",\\n'\n", - " ' \"content\": \"User mentions having to go to work soon and '\n", - " 'dealing with an Nvidia driver issue. The assistant offers '\n", - " 'troubleshooting advice for Nvidia drivers on Linux and wishes the '\n", - " 'user good luck with work.\"\\n'\n", + " ' \"name\": \"summary\",\\n'\n", + " ' \"content\": \"The user mentioned having to go to work soon and '\n", + " 'experiencing issues with Nvidia drivers on Linux. The assistant '\n", + " 'provided suggestions for resolving driver compatibility issues.\"\\n'\n", " ' },\\n'\n", " ' {\\n'\n", " ' \"role\": \"system\",\\n'\n", - " ' \"content\": \"User acknowledges the advice and says goodbye. '\n", - " 'The assistant wishes them well.\"\\n'\n", + " ' \"name\": \"summary\",\\n'\n", + " ' \"content\": \"The user said goodbye, indicating they would see '\n", + " 'the assistant later.\"\\n'\n", " ' }\\n'\n", " ']',\n", " 'role': 'assistant'}\n" @@ -676,7 +684,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -690,7 +698,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.12.1" } }, "nbformat": 4, diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 907669721..2edd42cf3 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1,4 +1,25 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "aiobotocore" +version = "2.15.2" +description = "Async client for aws services using botocore and aiohttp" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiobotocore-2.15.2-py3-none-any.whl", hash = "sha256:d4d3128b4b558e2b4c369bfa963b022d7e87303adb82eec623cec8aa77ae578a"}, + {file = "aiobotocore-2.15.2.tar.gz", hash = "sha256:9ac1cfcaccccc80602968174aa032bf978abe36bd4e55e6781d6500909af1375"}, +] + +[package.dependencies] +aiohttp = ">=3.9.2,<4.0.0" +aioitertools = ">=0.5.1,<1.0.0" +botocore = ">=1.35.16,<1.35.37" +wrapt = ">=1.10.10,<2.0.0" + +[package.extras] +awscli = ["awscli (>=1.34.16,<1.35.3)"] +boto3 = ["boto3 (>=1.35.16,<1.35.37)"] [[package]] name = "aiohappyeyeballs" @@ -13,102 +34,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.9" +version = "3.11.8" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8b3fb28a9ac8f2558760d8e637dbf27aef1e8b7f1d221e8669a1074d1a266bb2"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91aa966858593f64c8a65cdefa3d6dc8fe3c2768b159da84c1ddbbb2c01ab4ef"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63649309da83277f06a15bbdc2a54fbe75efb92caa2c25bb57ca37762789c746"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e7fabedb3fe06933f47f1538df7b3a8d78e13d7167195f51ca47ee12690373"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c070430fda1a550a1c3a4c2d7281d3b8cfc0c6715f616e40e3332201a253067"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d0a4901b27272ae54e42067bc4b9a90e619a690b4dc43ea5950eb3070afc32"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fec5fac7aea6c060f317f07494961236434928e6f4374e170ef50b3001e14581"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:172ad884bb61ad31ed7beed8be776eb17e7fb423f1c1be836d5cb357a096bf12"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d646fdd74c25bbdd4a055414f0fe32896c400f38ffbdfc78c68e62812a9e0257"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e86260b76786c28acf0b5fe31c8dca4c2add95098c709b11e8c35b424ebd4f5b"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d7cafc11d70fdd8801abfc2ff276744ae4cb39d8060b6b542c7e44e5f2cfc2"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc262c3df78c8ff6020c782d9ce02e4bcffe4900ad71c0ecdad59943cba54442"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:482c85cf3d429844396d939b22bc2a03849cb9ad33344689ad1c85697bcba33a"}, - {file = "aiohttp-3.10.9-cp310-cp310-win32.whl", hash = "sha256:aeebd3061f6f1747c011e1d0b0b5f04f9f54ad1a2ca183e687e7277bef2e0da2"}, - {file = "aiohttp-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:fa430b871220dc62572cef9c69b41e0d70fcb9d486a4a207a5de4c1f25d82593"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16e6a51d8bc96b77f04a6764b4ad03eeef43baa32014fce71e882bd71302c7e4"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8bd9125dd0cc8ebd84bff2be64b10fdba7dc6fd7be431b5eaf67723557de3a31"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dcf354661f54e6a49193d0b5653a1b011ba856e0b7a76bda2c33e4c6892f34ea"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42775de0ca04f90c10c5c46291535ec08e9bcc4756f1b48f02a0657febe89b10"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d1e4185c5d7187684d41ebb50c9aeaaaa06ca1875f4c57593071b0409d2444"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2695c61cf53a5d4345a43d689f37fc0f6d3a2dc520660aec27ec0f06288d1f9"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a3f063b41cc06e8d0b3fcbbfc9c05b7420f41287e0cd4f75ce0a1f3d80729e6"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d37f4718002863b82c6f391c8efd4d3a817da37030a29e2682a94d2716209de"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2746d8994ebca1bdc55a1e998feff4e94222da709623bb18f6e5cfec8ec01baf"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6f3c6648aa123bcd73d6f26607d59967b607b0da8ffcc27d418a4b59f4c98c7c"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:558b3d223fd631ad134d89adea876e7fdb4c93c849ef195049c063ada82b7d08"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4e6cb75f8ddd9c2132d00bc03c9716add57f4beff1263463724f6398b813e7eb"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:608cecd8d58d285bfd52dbca5b6251ca8d6ea567022c8a0eaae03c2589cd9af9"}, - {file = "aiohttp-3.10.9-cp311-cp311-win32.whl", hash = "sha256:36d4fba838be5f083f5490ddd281813b44d69685db910907636bc5dca6322316"}, - {file = "aiohttp-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:8be1a65487bdfc285bd5e9baf3208c2132ca92a9b4020e9f27df1b16fab998a9"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4fd16b30567c5b8e167923be6e027eeae0f20cf2b8a26b98a25115f28ad48ee0"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40ff5b7660f903dc587ed36ef08a88d46840182d9d4b5694e7607877ced698a1"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4edc3fd701e2b9a0d605a7b23d3de4ad23137d23fc0dbab726aa71d92f11aaaf"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e525b69ee8a92c146ae5b4da9ecd15e518df4d40003b01b454ad694a27f498b5"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5002a02c17fcfd796d20bac719981d2fca9c006aac0797eb8f430a58e9d12431"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4ceeae2fb8cabdd1b71c82bfdd39662473d3433ec95b962200e9e752fb70d0"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6e395c3d1f773cf0651cd3559e25182eb0c03a2777b53b4575d8adc1149c6e9"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbdb8def5268f3f9cd753a265756f49228a20ed14a480d151df727808b4531dd"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f82ace0ec57c94aaf5b0e118d4366cff5889097412c75aa14b4fd5fc0c44ee3e"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ebdc3b3714afe1b134b3bbeb5f745eed3ecbcff92ab25d80e4ef299e83a5465"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f9ca09414003c0e96a735daa1f071f7d7ed06962ef4fa29ceb6c80d06696d900"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1298b854fd31d0567cbb916091be9d3278168064fca88e70b8468875ef9ff7e7"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60ad5b8a7452c0f5645c73d4dad7490afd6119d453d302cd5b72b678a85d6044"}, - {file = "aiohttp-3.10.9-cp312-cp312-win32.whl", hash = "sha256:1a0ee6c0d590c917f1b9629371fce5f3d3f22c317aa96fbdcce3260754d7ea21"}, - {file = "aiohttp-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:c46131c6112b534b178d4e002abe450a0a29840b61413ac25243f1291613806a"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bd9f3eac515c16c4360a6a00c38119333901b8590fe93c3257a9b536026594d"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8cc0d13b4e3b1362d424ce3f4e8c79e1f7247a00d792823ffd640878abf28e56"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba1a599255ad6a41022e261e31bc2f6f9355a419575b391f9655c4d9e5df5ff5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:776e9f3c9b377fcf097c4a04b241b15691e6662d850168642ff976780609303c"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8debb45545ad95b58cc16c3c1cc19ad82cffcb106db12b437885dbee265f0ab5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2555e4949c8d8782f18ef20e9d39730d2656e218a6f1a21a4c4c0b56546a02e"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c54dc329cd44f7f7883a9f4baaefe686e8b9662e2c6c184ea15cceee587d8d69"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e709d6ac598c5416f879bb1bae3fd751366120ac3fa235a01de763537385d036"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:17c272cfe7b07a5bb0c6ad3f234e0c336fb53f3bf17840f66bd77b5815ab3d16"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c21c82df33b264216abffff9f8370f303dab65d8eee3767efbbd2734363f677"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9331dd34145ff105177855017920dde140b447049cd62bb589de320fd6ddd582"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ac3196952c673822ebed8871cf8802e17254fff2a2ed4835d9c045d9b88c5ec7"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2c33fa6e10bb7ed262e3ff03cc69d52869514f16558db0626a7c5c61dde3c29f"}, - {file = "aiohttp-3.10.9-cp313-cp313-win32.whl", hash = "sha256:a14e4b672c257a6b94fe934ee62666bacbc8e45b7876f9dd9502d0f0fe69db16"}, - {file = "aiohttp-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:a35ed3d03910785f7d9d6f5381f0c24002b2b888b298e6f941b2fc94c5055fcd"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f392ef50e22c31fa49b5a46af7f983fa3f118f3eccb8522063bee8bfa6755f8"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d1f5c9169e26db6a61276008582d945405b8316aae2bb198220466e68114a0f5"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8d9d10d10ec27c0d46ddaecc3c5598c4db9ce4e6398ca872cdde0525765caa2f"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97273a52d7f89a75b11ec386f786d3da7723d7efae3034b4dda79f6f093edc1"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d271f770b52e32236d945911b2082f9318e90ff835d45224fa9e28374303f729"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7003f33f5f7da1eb02f0446b0f8d2ccf57d253ca6c2e7a5732d25889da82b517"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6e00c8a92e7663ed2be6fcc08a2997ff06ce73c8080cd0df10cc0321a3168d7"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a61df62966ce6507aafab24e124e0c3a1cfbe23c59732987fc0fd0d71daa0b88"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:60555211a006d26e1a389222e3fab8cd379f28e0fbf7472ee55b16c6c529e3a6"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d15a29424e96fad56dc2f3abed10a89c50c099f97d2416520c7a543e8fddf066"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a19caae0d670771ea7854ca30df76f676eb47e0fd9b2ee4392d44708f272122d"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:99f9678bf0e2b1b695e8028fedac24ab6770937932eda695815d5a6618c37e04"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2914caa46054f3b5ff910468d686742ff8cff54b8a67319d75f5d5945fd0a13d"}, - {file = "aiohttp-3.10.9-cp38-cp38-win32.whl", hash = "sha256:0bc059ecbce835630e635879f5f480a742e130d9821fbe3d2f76610a6698ee25"}, - {file = "aiohttp-3.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:e883b61b75ca6efc2541fcd52a5c8ccfe288b24d97e20ac08fdf343b8ac672ea"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcd546782d03181b0b1d20b43d612429a90a68779659ba8045114b867971ab71"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:85711eec2d875cd88c7eb40e734c4ca6d9ae477d6f26bd2b5bb4f7f60e41b156"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02d1d6610588bcd743fae827bd6f2e47e0d09b346f230824b4c6fb85c6065f9c"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3668d0c2a4d23fb136a753eba42caa2c0abbd3d9c5c87ee150a716a16c6deec1"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7c071235a47d407b0e93aa6262b49422dbe48d7d8566e1158fecc91043dd948"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac74e794e3aee92ae8f571bfeaa103a141e409863a100ab63a253b1c53b707eb"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bbf94d4a0447705b7775417ca8bb8086cc5482023a6e17cdc8f96d0b1b5aba6"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb0b2d5d51f96b6cc19e6ab46a7b684be23240426ae951dcdac9639ab111b45e"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e83dfefb4f7d285c2d6a07a22268344a97d61579b3e0dce482a5be0251d672ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f0a44bb40b6aaa4fb9a5c1ee07880570ecda2065433a96ccff409c9c20c1624a"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c2b627d3c8982691b06d89d31093cee158c30629fdfebe705a91814d49b554f8"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:03690541e4cc866eef79626cfa1ef4dd729c5c1408600c8cb9e12e1137eed6ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad3675c126f2a95bde637d162f8231cff6bc0bc9fbe31bd78075f9ff7921e322"}, - {file = "aiohttp-3.10.9-cp39-cp39-win32.whl", hash = "sha256:1321658f12b6caffafdc35cfba6c882cb014af86bef4e78c125e7e794dfb927b"}, - {file = "aiohttp-3.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:9fdf5c839bf95fc67be5794c780419edb0dbef776edcfc6c2e5e2ffd5ee755fa"}, - {file = "aiohttp-3.10.9.tar.gz", hash = "sha256:143b0026a9dab07a05ad2dd9e46aa859bffdd6348ddc5967b42161168c24f857"}, + {file = "aiohttp-3.11.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2d2ca685c6a851ce64e511fbcb906e4dd97d13e567ca7ecb5cb30b184e15dc6d"}, + {file = "aiohttp-3.11.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52913bb8a0a72a57479f54b281300c9d23036aa9aa3ebbc9a32a643484eadfc2"}, + {file = "aiohttp-3.11.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:35dafc70051b6cbd6dafb533b4e3f0df6225a4896be373ef86367b2987409331"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:561b9596a9f90266673ef0b950c27e04ab597cdb53785e2ac91b83b33c31b509"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d479c1fdcc920056a06d04059db52eb8590ecbbb3acdcaeeea26a88ff782e94a"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ce8eb6444bb6e862feca664ce365afa8e2e32db24dcf1a502719a8a002f9274"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df9bf08eb93611b1d4d6245b6fecf88728e90eece00e00d554e1b0c445557d83"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a20ddaa58fea717177fac9a4a1fb8b39be868aa4fed2af6de4313b7a08f0f71"}, + {file = "aiohttp-3.11.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9f4aadfea6b48cfa17aef1a68ba6bee5a0246374f5a588e299a4f4ff5bd1c77b"}, + {file = "aiohttp-3.11.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:aa7deebb4bc5143745e6282139d7b9de50beb6d06609df64d2c993ef496bc7eb"}, + {file = "aiohttp-3.11.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fe503a76b9e3a13b62e64545693c9463afe9d429e0909120f7bb66de91ed8bc2"}, + {file = "aiohttp-3.11.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1c5838a68e31712354129add1b5fe32b06aa05275f835130edc650e6288af05f"}, + {file = "aiohttp-3.11.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:832e58d9454fe501b0d092cdf660c0e34e16005f61acd06e1c79b0fc45019c94"}, + {file = "aiohttp-3.11.8-cp310-cp310-win32.whl", hash = "sha256:00618c37a350884c08e87cf9a6532be274d564227ac49e0b474cf41f27e1f190"}, + {file = "aiohttp-3.11.8-cp310-cp310-win_amd64.whl", hash = "sha256:8eeaac75203da1a54afe1faea3c855a1973026b54929112aa9b67bceadbcb0ca"}, + {file = "aiohttp-3.11.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8dd02b44555893adfe7cc4b3b454fee04f9dcec45cf66ef5bb53ebf393f0505"}, + {file = "aiohttp-3.11.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:658052941324edea3dee1f681375e70779f55e437e07bdfc4b5bbe65ad53cefb"}, + {file = "aiohttp-3.11.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c829471a9e2266da4a0666f8a9e215f19320f79778af379c1c7db324ac24ed2"}, + {file = "aiohttp-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d21951756690f5d86d0215da38eb0fd65def03b5e2a1c08a4a39718a6d0d48f2"}, + {file = "aiohttp-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2fa50ddc6b21cc1ae23e13524d6f75b27e279fdf5cf905b2df6fd171891ac4e2"}, + {file = "aiohttp-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a5afbd805e449048ecebb1a256176e953d4ca9e48bab387d4d1c8524f1c7a95"}, + {file = "aiohttp-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea68db69f2a4ddc24b28b8e754fc0b963ed7f9b9a76137f06fe44643d6821fbd"}, + {file = "aiohttp-3.11.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b3ac163145660ce660aed2f1005e6d4de840d39728990b7250525eeec4e4a8"}, + {file = "aiohttp-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e9ac0cce897904b77e109e5403ed713187dbdf96832bfd061ac07164264be16c"}, + {file = "aiohttp-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3260c77cff4e35245bc517658bd54d7a64787f71f3c4f723877c82f22835b032"}, + {file = "aiohttp-3.11.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7fd9c11ffad6b022bf02a41a70418cb2ab3b33f2c27842a5999e3ab78daf280"}, + {file = "aiohttp-3.11.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16bda233a7b159ab08107e8858fedca90a9de287057fab54cafde51bd83f9819"}, + {file = "aiohttp-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4867008617bbf86e9fb5b00f72dd0e3a00a579b32233caff834320867f9b7cac"}, + {file = "aiohttp-3.11.8-cp311-cp311-win32.whl", hash = "sha256:17e6b9d8e29e3bfc7f893f327e92c9769d3582cee2fb1652c1431ac3f60115a0"}, + {file = "aiohttp-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:7f3be4961a5c2c670f31caab7641a37ea2a97031f0d8ae15bcfd36b6bf273200"}, + {file = "aiohttp-3.11.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0e3b5bfef913d6be270c81976fbc0cbf66625cd92663bbb7e03b3adbd6aa4ac6"}, + {file = "aiohttp-3.11.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb51a81cb637b9a072c9cfae1839e35c6579638861eb3479eb5d6e6ce8bc6782"}, + {file = "aiohttp-3.11.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd2ca84e5f7a35f313a62eb7d6a50bac6760b60bafce34586750712731c0aeff"}, + {file = "aiohttp-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47c6663df9446aa848b478413219600da4b54bc0409e1ac4bc80fb1a81501363"}, + {file = "aiohttp-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c665ed4b52256614858b20711bbbd2755b0e19ec86870f8ff1645acf9ae9e760"}, + {file = "aiohttp-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35d4545e7684da7a954ffc2dce495462cb16a902dffdebe98572408f6aaaee83"}, + {file = "aiohttp-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85be3899e6860dd2cd3f4370ded6708e939d00d5ec922a8eb328d114db605a47"}, + {file = "aiohttp-3.11.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ed9f1f2697713c48efc9ec483ad5d062e4aa91854f090a3eba0b19c002851d"}, + {file = "aiohttp-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c0dbae99737badf3f5e862088a118e28d3b36f03eb608a6382eddfd68178e05b"}, + {file = "aiohttp-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:beae08f900b2980af4353a0200eb162b39f276fd8a6e43079a540f83964671f4"}, + {file = "aiohttp-3.11.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d6f9e5fd1b3ecbaca3e04a15a02d1fa213248608caee99fd5bdddd4759959cf7"}, + {file = "aiohttp-3.11.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7def89a41fe32120d89cd4577f5efbab3c52234c5890066ced8a2f7202dff88"}, + {file = "aiohttp-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:98f596cf59292e779bc387f22378a3d2c5e052c9fe2bf822ac4f547c6fe57758"}, + {file = "aiohttp-3.11.8-cp312-cp312-win32.whl", hash = "sha256:b64fa6b76b35b695cd3e5c42a4e568cbea8d41c9e59165e2a43da00976e2027e"}, + {file = "aiohttp-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:afba47981ff73b1794c00dce774334dcfe62664b3b4f78f278b77d21ce9daf43"}, + {file = "aiohttp-3.11.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a81525430da5ca356fae6e889daeb6f5cc0d5f0cef88e59cdde48e2394ea1365"}, + {file = "aiohttp-3.11.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7565689e86a88c1d258351ebd14e343337b76a56ca5c0a2c1db96ec28149386f"}, + {file = "aiohttp-3.11.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0f9dbe9763c014c408ad51a027dc9582518e992dc63e2ffe359ac1b4840a560"}, + {file = "aiohttp-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca580edc3ccd7f6ea76ad9cf59f5a8756d338e770b5eda7be26bcda8fa7ef53"}, + {file = "aiohttp-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d141631a7348038fc7b5d1a81b3c9afa9aa056188ded7902fe754028fdea5c5"}, + {file = "aiohttp-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64e6b14608a56a4c76c60daac730b0c0eeaf9d10dfc3231f7fc26521a0d628fd"}, + {file = "aiohttp-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0983d0ce329f2f9dbeb355c3744bd6333f34e0dc56025b6b7d4f285b90acb51e"}, + {file = "aiohttp-3.11.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d96b93a46a3742880fa21bcb35c6c40cf27714ec0fb8ec85fe444d73b95131b9"}, + {file = "aiohttp-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f4f1779c3142d913c509c2ed1de8b8f920e07a5cd65ac1f57c61cfb6bfded5a4"}, + {file = "aiohttp-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:48be7cff468c9c0d86a02e6a826e1fe159094b16d5aa2c17703e7317f791b0f9"}, + {file = "aiohttp-3.11.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:daea456b79ca2bacc7f062845bbb1139c3b3231fc83169da5a682cf385416dd1"}, + {file = "aiohttp-3.11.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c92e763cf641e10ad9342597d20060ba23de5e411aada96660e679e3f9371189"}, + {file = "aiohttp-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a750ee5a177e0f873d6b2d7d0fa6e1e7c658fc0ca8ea56438dcba2ac94bedb09"}, + {file = "aiohttp-3.11.8-cp313-cp313-win32.whl", hash = "sha256:4448c9c7f77bad48a6569062c0c16deb77fbb7363de1dc71ed087f66fb3b3c96"}, + {file = "aiohttp-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:481075a1949de79a8a6841e0086f2f5f464785c592cf527ed0db2c0cbd0e1ba2"}, + {file = "aiohttp-3.11.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:72779bfb34d6d6b51e55a7f4901b410e416b5431738b367d49696928c91a2ca8"}, + {file = "aiohttp-3.11.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e6523f39071a01757048985e4cc22d04aa130bc40d9128503f3a61a3ee98328"}, + {file = "aiohttp-3.11.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:220bbce18b3046973465be45415430f1cab39d7fdc40cbcf0a8c05485c6902fe"}, + {file = "aiohttp-3.11.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336bbf7a33dd8cb4a7afb98c70e9935a81e5e88f7ac595ba2e84b1fb5da190d6"}, + {file = "aiohttp-3.11.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c5e4f1ba5059b85e05c551961a448ce2689c6249ed6a2e2174796842c191d10"}, + {file = "aiohttp-3.11.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9f9fd5c672c962389429abd11ed32c9c93f7932fd58584cae1e43951b141c6b"}, + {file = "aiohttp-3.11.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58bd94ad48143e1d42e05fc055da41de0a9933f378ad87760595b8aec83d317b"}, + {file = "aiohttp-3.11.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf52642b12d70d78c18882915201bc5345f7c8f0f2ab8919d99b886aa6475a7"}, + {file = "aiohttp-3.11.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fee12d8487b0df2b683424cca2a0d8fb7281d5607518d742e98119a74af01026"}, + {file = "aiohttp-3.11.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:65fd04f1fea668ad1af48ac31b752000e222dccffedcad3de8ccf9d34489ccd3"}, + {file = "aiohttp-3.11.8-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c3f397e0511a0ec4fe331e602fc057dfd336d352062deb9969ebd81e253a149c"}, + {file = "aiohttp-3.11.8-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:cf8f05f4abe3288fe2e106e1461fd20d8abf6103886ddfb6d746a5b8fb830d2b"}, + {file = "aiohttp-3.11.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7d71d4ac0792ff89541179394d303be846a0b6cd3821ae67286ee69ecec16f9f"}, + {file = "aiohttp-3.11.8-cp39-cp39-win32.whl", hash = "sha256:2b6f8716044ae5e5f2a3b4e4b6bfee48e97c8b2a92e56f43aadd728c7fd26b7d"}, + {file = "aiohttp-3.11.8-cp39-cp39-win_amd64.whl", hash = "sha256:da343903214bf9f9d314b913caa499fa19e26d73e6e23a3db7d4898ea6d47028"}, + {file = "aiohttp-3.11.8.tar.gz", hash = "sha256:7bc9d64a2350cbb29a9732334e1a0743cbb6844de1731cbdf5949b235653f3fd"}, ] [package.dependencies] @@ -117,11 +123,27 @@ aiosignal = ">=1.1.2" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.12.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +[[package]] +name = "aioitertools" +version = "0.12.0" +description = "itertools and builtins for AsyncIO and mixed iterables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, + {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, +] + +[package.extras] +dev = ["attribution (==1.8.0)", "black (==24.8.0)", "build (>=1.2)", "coverage (==7.6.1)", "flake8 (==7.1.1)", "flit (==3.9.0)", "mypy (==1.11.2)", "ufmt (==2.7.1)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==8.0.2)", "sphinx-mdinclude (==0.6.2)"] + [[package]] name = "aiosignal" version = "1.3.1" @@ -147,15 +169,40 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[[package]] +name = "anthropic" +version = "0.37.1" +description = "The official Python library for the anthropic API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anthropic-0.37.1-py3-none-any.whl", hash = "sha256:8f550f88906823752e2abf99fbe491fbc8d40bce4cb26b9663abdf7be990d721"}, + {file = "anthropic-0.37.1.tar.gz", hash = "sha256:99f688265795daa7ba9256ee68eaf2f05d53cd99d7417f4a0c2dc292c106d00a"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tokenizers = ">=0.13.0" +typing-extensions = ">=4.7,<5" + +[package.extras] +bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"] +vertex = ["google-auth (>=2,<3)"] + [[package]] name = "anyio" -version = "4.6.0" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, - {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -164,7 +211,7 @@ sniffio = ">=1.1" [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -180,13 +227,13 @@ files = [ [[package]] name = "argcomplete" -version = "3.5.0" +version = "3.5.1" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" files = [ - {file = "argcomplete-3.5.0-py3-none-any.whl", hash = "sha256:d4bcf3ff544f51e16e54228a7ac7f486ed70ebf2ecfe49a63a91171c76bf029b"}, - {file = "argcomplete-3.5.0.tar.gz", hash = "sha256:4349400469dccfb7950bb60334a680c58d88699bff6159df61251878dc6bf74b"}, + {file = "argcomplete-3.5.1-py3-none-any.whl", hash = "sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363"}, + {file = "argcomplete-3.5.1.tar.gz", hash = "sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4"}, ] [package.extras] @@ -371,33 +418,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.8.0" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -409,27 +456,97 @@ platformdirs = ">=2" [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bleach" -version = "6.1.0" +version = "6.2.0" description = "An easy safelist-based HTML-sanitizing tool." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, - {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, + {file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"}, + {file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"}, ] [package.dependencies] -six = ">=1.9.0" webencodings = "*" [package.extras] -css = ["tinycss2 (>=1.1.0,<1.3)"] +css = ["tinycss2 (>=1.1.0,<1.5)"] + +[[package]] +name = "blis" +version = "1.0.1" +description = "The Blis BLAS-like linear algebra library, as a self-contained C-extension." +optional = false +python-versions = "*" +files = [ + {file = "blis-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:232f68f78fd9ab2f8bf01424cd6a265edd97e14e26186ef48eca1b6f212e928f"}, + {file = "blis-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90a796d8311b6439d63c91ed09163be995173a1ca5bedc510c1b45cbdbb7886c"}, + {file = "blis-1.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:388b0b0c55df884eb5853292d53c7c3daaa009b3d991e7a95413e46e82e88058"}, + {file = "blis-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c89149eed90c3b76f50c88d8d68661ca07d8b4bfaa517eedf5b77ddf640bb1"}, + {file = "blis-1.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f23cff163c0a256b580c33364f1598a4f49f532074c673a734d9b02a101adce1"}, + {file = "blis-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8d4d403d4b4979d5b1e6f12637ed25fb3bd4e260440997d9ba9e752f9e9c91cb"}, + {file = "blis-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6440bed28f405ab81d1d57b3fb96b7ea8b5b1f3d3a0d29860b0a814fe4ece27"}, + {file = "blis-1.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5076b33c04b864709d0d22c40e9f57dc06ee9c2bd2f7ab72b10ffa74a470d9e6"}, + {file = "blis-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6b1f791fa3eeb267b97f5877a6bdcc09c868603b84422acf7fd138ec9b96c3c"}, + {file = "blis-1.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c249649c0f7c06b2368a9d5c6b12209255981e96304c6140a3beffaff6cae62"}, + {file = "blis-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:632580f1d3ff44fb36e8a21a9457f23aeaff5d35d108bd2ef0393b9f6d85de93"}, + {file = "blis-1.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:64393297304712818020734fa75735f4543243eefc44858ef3c99375d5bb029a"}, + {file = "blis-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d3b3293c795007dbf4ba8ceaf3184a6bf510ca3252d0229607792f52e8702bb2"}, + {file = "blis-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:60e1b03663bee7a37b4b3131b4179d283868ccb10a3260fed01dd980866bc49f"}, + {file = "blis-1.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e031b6cf0dcc026d697a860bf53bc02b5f26ddb5a3ecd23933c51cf22803825b"}, + {file = "blis-1.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f860c1723928c40d4920a05130d21a600dcb5fbf07aa1fe8f4bdff2c4a5238a5"}, + {file = "blis-1.0.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:399196506829c278836028511f351f32f2c992263e91ef876c6bc41dc2483d3d"}, + {file = "blis-1.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce20590af2c6ff02d66ffed4148ea8ea1e3f772febb8471e3e58614e71d428c1"}, + {file = "blis-1.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e6ad60d9cd81523429f46109b31e3c4bbdd0dc28a328d6dbdcdff8447a53a61e"}, + {file = "blis-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb553b233fc815957c5bbb5d2fc2f6d2b199c123ec15c5000db935662849e543"}, + {file = "blis-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:376188493f590c4310ca534b687ef96c21c8224eb1ef4a0420703eebe175d6fa"}, + {file = "blis-1.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:136eae35dd9fd6c1923b95d5623aa174abd43d5b764bed79fd37bf6ad40282e7"}, + {file = "blis-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:16b5a418c55825d0d7912f63befee865da7522ce8f388b8ef76224050c5f8386"}, + {file = "blis-1.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:806ab0838efa5e434e6fcdce86542601d0263256d483623bc86e91728a645de4"}, + {file = "blis-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ad60cde863a7cf8e69fcf0c3b965ab0f804f0332adb35552788bb76660c97f"}, + {file = "blis-1.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:40459f37f430de0c6059631cc9f7588c3bccb6ade074571015ea09856b91cb69"}, + {file = "blis-1.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f0977eaba2d3f64df649201aa281cf3216e7bec18929a7374c3f3f36100db029"}, + {file = "blis-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:5b4c8298c49b25058462731139b711e35e73623280cee03c3b0804cbdee50c0d"}, + {file = "blis-1.0.1.tar.gz", hash = "sha256:91739cd850ca8100dcddbd8ad66942cab20c9473cdea9a35b165b11d7b8d91e4"}, +] + +[package.dependencies] +numpy = ">=2.0.0,<3.0.0" + +[[package]] +name = "botocore" +version = "1.35.36" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.8" +files = [ + {file = "botocore-1.35.36-py3-none-any.whl", hash = "sha256:64241c778bf2dc863d93abab159e14024d97a926a5715056ef6411418cb9ead3"}, + {file = "botocore-1.35.36.tar.gz", hash = "sha256:354ec1b766f0029b5d6ff0c45d1a0f9e5007b7d2f3ec89bcdd755b208c5bc797"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.22.0)"] + +[[package]] +name = "catalogue" +version = "2.0.10" +description = "Super lightweight function registries for your library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f"}, + {file = "catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15"}, +] [[package]] name = "certifi" @@ -523,101 +640,116 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -667,6 +799,23 @@ click = "*" [package.extras] test = ["pytest"] +[[package]] +name = "cloudpathlib" +version = "0.20.0" +description = "pathlib-style classes for cloud storage services." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cloudpathlib-0.20.0-py3-none-any.whl", hash = "sha256:7af3bcefbf73392ae7f31c08b3660ec31607f8c01b7f6262d4d73469a845f641"}, + {file = "cloudpathlib-0.20.0.tar.gz", hash = "sha256:f6ef7ca409a510f7ba4639ba50ab3fc5b6dee82d6dff0d7f5715fd0c9ab35891"}, +] + +[package.extras] +all = ["cloudpathlib[azure]", "cloudpathlib[gs]", "cloudpathlib[s3]"] +azure = ["azure-storage-blob (>=12)", "azure-storage-file-datalake (>=12)"] +gs = ["google-cloud-storage"] +s3 = ["boto3 (>=1.34.0)"] + [[package]] name = "colorama" version = "0.4.6" @@ -695,6 +844,21 @@ traitlets = ">=4" [package.extras] test = ["pytest"] +[[package]] +name = "confection" +version = "0.1.5" +description = "The sweetest config system for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "confection-0.1.5-py3-none-any.whl", hash = "sha256:e29d3c3f8eac06b3f77eb9dfb4bf2fc6bcc9622a98ca00a698e3d019c6430b14"}, + {file = "confection-0.1.5.tar.gz", hash = "sha256:8e72dd3ca6bd4f48913cd220f10b8275978e740411654b6e8ca6d7008c590f0e"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" +srsly = ">=2.4.0,<3.0.0" + [[package]] name = "cozo-embedded" version = "0.7.6" @@ -743,15 +907,50 @@ files = [ [package.extras] develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] +[[package]] +name = "cymem" +version = "2.0.10" +description = "Manage calls to calloc/free through Cython" +optional = false +python-versions = "*" +files = [ + {file = "cymem-2.0.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:010f78804cf5e2fbd08abad210d2b78a828bea1a9f978737e28e1614f5a258b4"}, + {file = "cymem-2.0.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9688f691518859e76c24c37686314dc5163f2fae1b9df264714220fc087b09a5"}, + {file = "cymem-2.0.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61ce538c594f348b90037b03910da31ce7aacca090ea64063593688c55f6adad"}, + {file = "cymem-2.0.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d45b99c727dfc303db3bb9f136b86731a4d231fbf9c27ce5745ea4a527da0b5"}, + {file = "cymem-2.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:a03abe0e2f8925707c3dee88060bea1a94b9a24afc7d07ee17f319022126bcb4"}, + {file = "cymem-2.0.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18dc5a7b6a325d5fc0b2b40beb02673f36f64655ee086649c91e44ce092c7b36"}, + {file = "cymem-2.0.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d30ce83ff9009e5c5c8186845d9d583f867dace88113089bfc0ee1c348e45d5a"}, + {file = "cymem-2.0.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce6cb07416c82633503974f331abde9e1514c90aae8b3240884e749c2a60adbc"}, + {file = "cymem-2.0.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34406e2bff8707719f3f4b262e50b04876369233d5277a7c2d0c2e73a8579b46"}, + {file = "cymem-2.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:51218af9645541005a1313d6640bf6e86e7fb4b38a87268a5ea428d50ac3cec2"}, + {file = "cymem-2.0.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6ed8b1ed448cd65e12405a02aa71b22a4094d8a623205625057c4c73ba4b133"}, + {file = "cymem-2.0.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e57928d9e93c61265281ea01a1d24499d397625b2766a0c5735b99bceb3ba75"}, + {file = "cymem-2.0.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4932060a5d55648fa4a3960f1cad9905572ed5c6f02af42f849e869d2803d4"}, + {file = "cymem-2.0.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f4bc6c823b400d32cddcfeefb3f352d52a0cc911cb0b5c1ef64e3f9741fd56b9"}, + {file = "cymem-2.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:6ae7f22af4bc4311f06c925df61c62219c11939dffc9c91d67caf89a7e1557a5"}, + {file = "cymem-2.0.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5698a515900dc697874444fa05d8d852bbad43543de2e7834ec3895156cc2aad"}, + {file = "cymem-2.0.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6580d657d0f208d675d62cc052fb908529d52d24282342e24a9843de85352b88"}, + {file = "cymem-2.0.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea72cf0e369f3cf1f10038d572143d88ce7c959222cf7d742acbeb45e00ac5c0"}, + {file = "cymem-2.0.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33d7f5014ad36af22995847fccd82ca0bd4b0394fb1d9dd9fef1e8cefdab2444"}, + {file = "cymem-2.0.10-cp313-cp313-win_amd64.whl", hash = "sha256:82f19a39052747309ced6b948b34aff62aa00c795c9d9d3d31a071e8c791efee"}, + {file = "cymem-2.0.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e644c3c48663d2c0580292e1d636e7eb8885bfe9df75f929d8ad0403621b75fe"}, + {file = "cymem-2.0.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f2bc8c69a23e3243e3a0c0feca08c9d4454d3cb7934bb11f5e1b3333151d69d"}, + {file = "cymem-2.0.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5369f1974854102ee1751577f13acbbb6a13ba73f9fbb44580f8f3275dae0205"}, + {file = "cymem-2.0.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ffb6181d589e65c46c2d515d8326746a2e0bda31b67c8b1edfbf0663249f84fb"}, + {file = "cymem-2.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:9805f7dbf078a0e2eb417b7e1166cedc590887b55e38a3f3ba5349649c93e6be"}, + {file = "cymem-2.0.10.tar.gz", hash = "sha256:f51700acfa1209b4a221dc892cca8030f4bc10d4c153dec098042f484c7f07a4"}, +] + [[package]] name = "datamodel-code-generator" -version = "0.25.9" +version = "0.26.3" description = "Datamodel Code Generator" optional = false -python-versions = "<4.0,>=3.7" +python-versions = "<4.0,>=3.8" files = [ - {file = "datamodel_code_generator-0.25.9-py3-none-any.whl", hash = "sha256:9e0324233123d6e39a35bc0004771956935889a974aacfd7a0651de11d2219a9"}, - {file = "datamodel_code_generator-0.25.9.tar.gz", hash = "sha256:65ca9807d8edbd88a7f7931c10f4bc1c08bd9bbc5bb0508418a2b6a16590eb65"}, + {file = "datamodel_code_generator-0.26.3-py3-none-any.whl", hash = "sha256:f1f8f1cef14f138fa239f987d4640837bb68d53c5f08d8673a7bde275b929fd8"}, + {file = "datamodel_code_generator-0.26.3.tar.gz", hash = "sha256:b58e0800eb6448e1d1df02f4586207c1e3631c4a188531d154b00b3cf2f95fd8"}, ] [package.dependencies] @@ -773,33 +972,37 @@ validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] [[package]] name = "debugpy" -version = "1.8.6" +version = "1.8.9" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b"}, - {file = "debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b"}, - {file = "debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9"}, - {file = "debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd"}, - {file = "debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955"}, - {file = "debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b"}, - {file = "debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43"}, - {file = "debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833"}, - {file = "debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128"}, - {file = "debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972"}, - {file = "debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c"}, - {file = "debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f"}, - {file = "debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb"}, - {file = "debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a"}, - {file = "debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8"}, - {file = "debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d"}, - {file = "debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa"}, - {file = "debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881"}, - {file = "debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123"}, - {file = "debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51"}, - {file = "debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f"}, - {file = "debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a"}, + {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"}, + {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"}, + {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"}, + {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"}, + {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"}, + {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"}, + {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"}, + {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"}, + {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"}, + {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"}, + {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"}, + {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"}, + {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"}, + {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"}, + {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"}, + {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"}, + {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"}, + {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"}, + {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"}, + {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"}, + {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"}, + {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"}, + {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"}, + {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"}, + {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"}, + {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"}, ] [[package]] @@ -870,6 +1073,20 @@ files = [ dnspython = ">=2.0.0" idna = ">=2.0.0" +[[package]] +name = "en_core_web_sm" +version = "3.8.0" +description = "English pipeline optimized for CPU. Components: tok2vec, tagger, parser, senter, ner, attribute_ruler, lemmatizer." +optional = false +python-versions = "*" +files = [ + {file = "en_core_web_sm-3.8.0-py3-none-any.whl", hash = "sha256:1932429db727d4bff3deed6b34cfc05df17794f4a52eeb26cf8928f7c1a0fb85"}, +] + +[package.source] +type = "url" +url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl" + [[package]] name = "environs" version = "10.3.0" @@ -907,18 +1124,18 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastapi" -version = "0.112.4" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.112.4-py3-none-any.whl", hash = "sha256:6d4f9c3301825d4620665cace8e2bc34e303f61c05a5382d1d61a048ea7f2f37"}, - {file = "fastapi-0.112.4.tar.gz", hash = "sha256:b1f72e1f72afe7902ccd639ba320abb5d57a309804f45c10ab0ce3693cadeb33"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.40.0,<0.42.0" typing-extensions = ">=4.8.0" [package.extras] @@ -927,13 +1144,13 @@ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.0" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.0-py3-none-any.whl", hash = "sha256:5b23b8e7c9c6adc0ecb91c03a0768cb48cd154d9159378a69c8318532e0b5cbf"}, + {file = "fastjsonschema-2.21.0.tar.gz", hash = "sha256:a02026bbbedc83729da3bfff215564b71902757f33f60089f1abae193daa4771"}, ] [package.extras] @@ -982,99 +1199,114 @@ files = [ [[package]] name = "frozenlist" -version = "1.4.1" +version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] [[package]] name = "fsspec" -version = "2024.9.0" +version = "2024.10.0" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, - {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, + {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"}, + {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"}, ] [package.extras] @@ -1176,6 +1408,27 @@ files = [ {file = "google_re2-1.1.20240702.tar.gz", hash = "sha256:8788db69f6c93cb229df62c74b2d9aa8e64bf754e9495700f85812afa32efd2b"}, ] +[[package]] +name = "gunicorn" +version = "23.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "h11" version = "0.14.0" @@ -1189,13 +1442,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -1235,13 +1488,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "huggingface-hub" -version = "0.25.1" +version = "0.26.3" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.25.1-py3-none-any.whl", hash = "sha256:a5158ded931b3188f54ea9028097312cb0acd50bffaaa2612014c3c526b44972"}, - {file = "huggingface_hub-0.25.1.tar.gz", hash = "sha256:9ff7cb327343211fbd06e2b149b8f362fd1e389454f3f14c6db75a4999ee20ff"}, + {file = "huggingface_hub-0.26.3-py3-none-any.whl", hash = "sha256:e66aa99e569c2d5419240a9e553ad07245a5b1300350bfbc5a4945cf7432991b"}, + {file = "huggingface_hub-0.26.3.tar.gz", hash = "sha256:90e1fe62ffc26757a073aaad618422b899ccf9447c2bba8c902a90bef5b42e1d"}, ] [package.dependencies] @@ -1254,16 +1507,16 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] hf-transfer = ["hf-transfer (>=0.1.4)"] -inference = ["aiohttp", "minijinja (>=1.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] +inference = ["aiohttp"] +quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"] tensorflow = ["graphviz", "pydot", "tensorflow"] tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] torch = ["safetensors[torch]", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] @@ -1283,13 +1536,13 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "immutabledict" -version = "4.2.0" +version = "4.2.1" description = "Immutable wrapper around dictionaries (a fork of frozendict)" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.8" files = [ - {file = "immutabledict-4.2.0-py3-none-any.whl", hash = "sha256:d728b2c2410d698d95e6200237feb50a695584d20289ad3379a439aa3d90baba"}, - {file = "immutabledict-4.2.0.tar.gz", hash = "sha256:e003fd81aad2377a5a758bf7e1086cf3b70b63e9a5cc2f46bce8d0a2b4727c5f"}, + {file = "immutabledict-4.2.1-py3-none-any.whl", hash = "sha256:c56a26ced38c236f79e74af3ccce53772827cef5c3bce7cab33ff2060f756373"}, + {file = "immutabledict-4.2.1.tar.gz", hash = "sha256:d91017248981c72eb66c8ff9834e99c2f53562346f23e7f51e7a5ebcf66a3bcc"}, ] [[package]] @@ -1379,13 +1632,13 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "8.28.0" +version = "8.30.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.28.0-py3-none-any.whl", hash = "sha256:530ef1e7bb693724d3cdc37287c80b07ad9b25986c007a53aa1857272dac3f35"}, - {file = "ipython-8.28.0.tar.gz", hash = "sha256:0d0d15ca1e01faeb868ef56bc7ee5a0de5bd66885735682e8a322ae289a13d1a"}, + {file = "ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321"}, + {file = "ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e"}, ] [package.dependencies] @@ -1394,15 +1647,15 @@ decorator = "*" jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt-toolkit = ">=3.0.41,<3.1.0" +prompt_toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" -stack-data = "*" +stack_data = "*" traitlets = ">=5.13.0" [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -1464,22 +1717,22 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" @@ -1513,85 +1766,127 @@ Jinja2 = ">=2.2" [[package]] name = "jiter" -version = "0.5.0" +version = "0.8.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" files = [ - {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, - {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc0d5b8b3dd12e91dd184b87273f864b363dfabc90ef29a1092d269f18c7e28"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c22541f0b672f4d741382a97c65609332a783501551445ab2df137ada01e019e"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63314832e302cc10d8dfbda0333a384bf4bcfce80d65fe99b0f3c0da8945a91a"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a25fbd8a5a58061e433d6fae6d5298777c0814a8bcefa1e5ecfff20c594bd749"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:503b2c27d87dfff5ab717a8200fbbcf4714516c9d85558048b1fc14d2de7d8dc"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d1f3d27cce923713933a844872d213d244e09b53ec99b7a7fdf73d543529d6d"}, - {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c95980207b3998f2c3b3098f357994d3fd7661121f30669ca7cb945f09510a87"}, - {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afa66939d834b0ce063f57d9895e8036ffc41c4bd90e4a99631e5f261d9b518e"}, - {file = "jiter-0.5.0-cp310-none-win32.whl", hash = "sha256:f16ca8f10e62f25fd81d5310e852df6649af17824146ca74647a018424ddeccf"}, - {file = "jiter-0.5.0-cp310-none-win_amd64.whl", hash = "sha256:b2950e4798e82dd9176935ef6a55cf6a448b5c71515a556da3f6b811a7844f1e"}, - {file = "jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553"}, - {file = "jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06"}, - {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403"}, - {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646"}, - {file = "jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb"}, - {file = "jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae"}, - {file = "jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a"}, - {file = "jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a"}, - {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e"}, - {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338"}, - {file = "jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4"}, - {file = "jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5"}, - {file = "jiter-0.5.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f04bc2fc50dc77be9d10f73fcc4e39346402ffe21726ff41028f36e179b587e6"}, - {file = "jiter-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f433a4169ad22fcb550b11179bb2b4fd405de9b982601914ef448390b2954f3"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad4a6398c85d3a20067e6c69890ca01f68659da94d74c800298581724e426c7e"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6baa88334e7af3f4d7a5c66c3a63808e5efbc3698a1c57626541ddd22f8e4fbf"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ece0a115c05efca597c6d938f88c9357c843f8c245dbbb53361a1c01afd7148"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:335942557162ad372cc367ffaf93217117401bf930483b4b3ebdb1223dbddfa7"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649b0ee97a6e6da174bffcb3c8c051a5935d7d4f2f52ea1583b5b3e7822fbf14"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4be354c5de82157886ca7f5925dbda369b77344b4b4adf2723079715f823989"}, - {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5206144578831a6de278a38896864ded4ed96af66e1e63ec5dd7f4a1fce38a3a"}, - {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8120c60f8121ac3d6f072b97ef0e71770cc72b3c23084c72c4189428b1b1d3b6"}, - {file = "jiter-0.5.0-cp38-none-win32.whl", hash = "sha256:6f1223f88b6d76b519cb033a4d3687ca157c272ec5d6015c322fc5b3074d8a5e"}, - {file = "jiter-0.5.0-cp38-none-win_amd64.whl", hash = "sha256:c59614b225d9f434ea8fc0d0bec51ef5fa8c83679afedc0433905994fb36d631"}, - {file = "jiter-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0af3838cfb7e6afee3f00dc66fa24695199e20ba87df26e942820345b0afc566"}, - {file = "jiter-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:550b11d669600dbc342364fd4adbe987f14d0bbedaf06feb1b983383dcc4b961"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489875bf1a0ffb3cb38a727b01e6673f0f2e395b2aad3c9387f94187cb214bbf"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b250ca2594f5599ca82ba7e68785a669b352156260c5362ea1b4e04a0f3e2389"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ea18e01f785c6667ca15407cd6dabbe029d77474d53595a189bdc813347218e"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462a52be85b53cd9bffd94e2d788a09984274fe6cebb893d6287e1c296d50653"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92cc68b48d50fa472c79c93965e19bd48f40f207cb557a8346daa020d6ba973b"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c834133e59a8521bc87ebcad773608c6fa6ab5c7a022df24a45030826cf10bc"}, - {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab3a71ff31cf2d45cb216dc37af522d335211f3a972d2fe14ea99073de6cb104"}, - {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cccd3af9c48ac500c95e1bcbc498020c87e1781ff0345dd371462d67b76643eb"}, - {file = "jiter-0.5.0-cp39-none-win32.whl", hash = "sha256:368084d8d5c4fc40ff7c3cc513c4f73e02c85f6009217922d0823a48ee7adf61"}, - {file = "jiter-0.5.0-cp39-none-win_amd64.whl", hash = "sha256:ce03f7b4129eb72f1687fa11300fbf677b02990618428934662406d2a76742a1"}, - {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, + {file = "jiter-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:dee4eeb293ffcd2c3b31ebab684dbf7f7b71fe198f8eddcdf3a042cc6e10205a"}, + {file = "jiter-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aad1e6e9b01cf0304dcee14db03e92e0073287a6297caf5caf2e9dbfea16a924"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:504099fb7acdbe763e10690d560a25d4aee03d918d6a063f3a761d8a09fb833f"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2373487caad7fe39581f588ab5c9262fc1ade078d448626fec93f4ffba528858"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c341ecc3f9bccde952898b0c97c24f75b84b56a7e2f8bbc7c8e38cab0875a027"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e48e7a336529b9419d299b70c358d4ebf99b8f4b847ed3f1000ec9f320e8c0c"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ee157a8afd2943be690db679f82fafb8d347a8342e8b9c34863de30c538d55"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7dceae3549b80087f913aad4acc2a7c1e0ab7cb983effd78bdc9c41cabdcf18"}, + {file = "jiter-0.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e29e9ecce53d396772590438214cac4ab89776f5e60bd30601f1050b34464019"}, + {file = "jiter-0.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fa1782f22d5f92c620153133f35a9a395d3f3823374bceddd3e7032e2fdfa0b1"}, + {file = "jiter-0.8.0-cp310-none-win32.whl", hash = "sha256:f754ef13b4e4f67a3bf59fe974ef4342523801c48bf422f720bd37a02a360584"}, + {file = "jiter-0.8.0-cp310-none-win_amd64.whl", hash = "sha256:796f750b65f5d605f5e7acaccc6b051675e60c41d7ac3eab40dbd7b5b81a290f"}, + {file = "jiter-0.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f6f4e645efd96b4690b9b6091dbd4e0fa2885ba5c57a0305c1916b75b4f30ff6"}, + {file = "jiter-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f61cf6d93c1ade9b8245c9f14b7900feadb0b7899dbe4aa8de268b705647df81"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0396bc5cb1309c6dab085e70bb3913cdd92218315e47b44afe9eace68ee8adaa"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62d0e42ec5dc772bd8554a304358220be5d97d721c4648b23f3a9c01ccc2cb26"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec4b711989860705733fc59fb8c41b2def97041cea656b37cf6c8ea8dee1c3f4"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859cc35bf304ab066d88f10a44a3251a9cd057fb11ec23e00be22206db878f4f"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5000195921aa293b39b9b5bc959d7fa658e7f18f938c0e52732da8e3cc70a278"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36050284c0abde57aba34964d3920f3d6228211b65df7187059bb7c7f143759a"}, + {file = "jiter-0.8.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a88f608e050cfe45c48d771e86ecdbf5258314c883c986d4217cc79e1fb5f689"}, + {file = "jiter-0.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:646cf4237665b2e13b4159d8f26d53f59bc9f2e6e135e3a508a2e5dd26d978c6"}, + {file = "jiter-0.8.0-cp311-none-win32.whl", hash = "sha256:21fe5b8345db1b3023052b2ade9bb4d369417827242892051244af8fae8ba231"}, + {file = "jiter-0.8.0-cp311-none-win_amd64.whl", hash = "sha256:30c2161c5493acf6b6c3c909973fb64ae863747def01cc7574f3954e0a15042c"}, + {file = "jiter-0.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d91a52d8f49ada2672a4b808a0c5c25d28f320a2c9ca690e30ebd561eb5a1002"}, + {file = "jiter-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c38cf25cf7862f61410b7a49684d34eb3b5bcbd7ddaf4773eea40e0bd43de706"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6189beb5c4b3117624be6b2e84545cff7611f5855d02de2d06ff68e316182be"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13fa849c0e30643554add089983caa82f027d69fad8f50acadcb21c462244ab"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7765ca159d0a58e8e0f8ca972cd6d26a33bc97b4480d0d2309856763807cd28"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b0befe7c6e9fc867d5bed21bab0131dfe27d1fa5cd52ba2bced67da33730b7d"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d6363d4c6f1052b1d8b494eb9a72667c3ef5f80ebacfe18712728e85327000"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a873e57009863eeac3e3969e4653f07031d6270d037d6224415074ac17e5505c"}, + {file = "jiter-0.8.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2582912473c0d9940791479fe1bf2976a34f212eb8e0a82ee9e645ac275c5d16"}, + {file = "jiter-0.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:646163201af42f55393ee6e8f6136b8df488253a6533f4230a64242ecbfe6048"}, + {file = "jiter-0.8.0-cp312-none-win32.whl", hash = "sha256:96e75c9abfbf7387cba89a324d2356d86d8897ac58c956017d062ad510832dae"}, + {file = "jiter-0.8.0-cp312-none-win_amd64.whl", hash = "sha256:ed6074552b4a32e047b52dad5ab497223721efbd0e9efe68c67749f094a092f7"}, + {file = "jiter-0.8.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:dd5e351cb9b3e676ec3360a85ea96def515ad2b83c8ae3a251ce84985a2c9a6f"}, + {file = "jiter-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba9f12b0f801ecd5ed0cec29041dc425d1050922b434314c592fc30d51022467"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7ba461c3681728d556392e8ae56fb44a550155a24905f01982317b367c21dd4"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a15ed47ab09576db560dbc5c2c5a64477535beb056cd7d997d5dd0f2798770e"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cef55042816d0737142b0ec056c0356a5f681fb8d6aa8499b158e87098f4c6f8"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:549f170215adeb5e866f10617c3d019d8eb4e6d4e3c6b724b3b8c056514a3487"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f867edeb279d22020877640d2ea728de5817378c60a51be8af731a8a8f525306"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aef8845f463093799db4464cee2aa59d61aa8edcb3762aaa4aacbec3f478c929"}, + {file = "jiter-0.8.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:d0d6e22e4062c3d3c1bf3594baa2f67fc9dcdda8275abad99e468e0c6540bc54"}, + {file = "jiter-0.8.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:079e62e64696241ac3f408e337aaac09137ed760ccf2b72b1094b48745c13641"}, + {file = "jiter-0.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74d2b56ed3da5760544df53b5f5c39782e68efb64dc3aa0bba4cc08815e6fae8"}, + {file = "jiter-0.8.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:798dafe108cba58a7bb0a50d4d5971f98bb7f3c974e1373e750de6eb21c1a329"}, + {file = "jiter-0.8.0-cp313-none-win32.whl", hash = "sha256:ca6d3064dfc743eb0d3d7539d89d4ba886957c717567adc72744341c1e3573c9"}, + {file = "jiter-0.8.0-cp313-none-win_amd64.whl", hash = "sha256:38caedda64fe1f04b06d7011fc15e86b3b837ed5088657bf778656551e3cd8f9"}, + {file = "jiter-0.8.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:bb5c8a0a8d081c338db22e5b8d53a89a121790569cbb85f7d3cfb1fe0fbe9836"}, + {file = "jiter-0.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:202dbe8970bfb166fab950eaab8f829c505730a0b33cc5e1cfb0a1c9dd56b2f9"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9046812e5671fdcfb9ae02881fff1f6a14d484b7e8b3316179a372cdfa1e8026"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e6ac56425023e52d65150918ae25480d0a1ce2a6bf5ea2097f66a2cc50f6d692"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dfcf97210c6eab9d2a1c6af15dd39e1d5154b96a7145d0a97fa1df865b7b834"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4e3c8444d418686f78c9a547b9b90031faf72a0a1a46bfec7fb31edbd889c0d"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6507011a299b7f578559084256405a8428875540d8d13530e00b688e41b09493"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0aae4738eafdd34f0f25c2d3668ce9e8fa0d7cb75a2efae543c9a69aebc37323"}, + {file = "jiter-0.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5d782e790396b13f2a7b36bdcaa3736a33293bdda80a4bf1a3ce0cd5ef9f15"}, + {file = "jiter-0.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc7f993bc2c4e03015445adbb16790c303282fce2e8d9dc3a3905b1d40e50564"}, + {file = "jiter-0.8.0-cp38-none-win32.whl", hash = "sha256:d4a8a6eda018a991fa58ef707dd51524055d11f5acb2f516d70b1be1d15ab39c"}, + {file = "jiter-0.8.0-cp38-none-win_amd64.whl", hash = "sha256:4cca948a3eda8ea24ed98acb0ee19dc755b6ad2e570ec85e1527d5167f91ff67"}, + {file = "jiter-0.8.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ef89663678d8257063ce7c00d94638e05bd72f662c5e1eb0e07a172e6c1a9a9f"}, + {file = "jiter-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c402ddcba90b4cc71db3216e8330f4db36e0da2c78cf1d8a9c3ed8f272602a94"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a6dfe795b7a173a9f8ba7421cdd92193d60c1c973bbc50dc3758a9ad0fa5eb6"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ec29a31b9abd6be39453a2c45da067138a3005d65d2c0507c530e0f1fdcd9a4"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a488f8c54bddc3ddefaf3bfd6de4a52c97fc265d77bc2dcc6ee540c17e8c342"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeb5561adf4d26ca0d01b5811b4d7b56a8986699a473d700757b4758ef787883"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab961858d7ad13132328517d29f121ae1b2d94502191d6bcf96bddcc8bb5d1c"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a207e718d114d23acf0850a2174d290f42763d955030d9924ffa4227dbd0018f"}, + {file = "jiter-0.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:733bc9dc8ff718a0ae4695239e9268eb93e88b73b367dfac3ec227d8ce2f1e77"}, + {file = "jiter-0.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1ec27299e22d05e13a06e460bf7f75f26f9aaa0e0fb7d060f40e88df1d81faa"}, + {file = "jiter-0.8.0-cp39-none-win32.whl", hash = "sha256:e8dbfcb46553e6661d3fc1f33831598fcddf73d0f67834bce9fc3e9ebfe5c439"}, + {file = "jiter-0.8.0-cp39-none-win_amd64.whl", hash = "sha256:af2ce2487b3a93747e2cb5150081d4ae1e5874fce5924fc1a12e9e768e489ad8"}, + {file = "jiter-0.8.0.tar.gz", hash = "sha256:86fee98b569d4cc511ff2e3ec131354fafebd9348a487549c31ad371ae730310"}, +] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] [[package]] name = "json5" -version = "0.9.25" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + +[[package]] +name = "jsonpatch" +version = "1.33" +description = "Apply JSON-Patches (RFC 6902)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, + {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, +] + +[package.dependencies] +jsonpointer = ">=1.9" + [[package]] name = "jsonpointer" version = "3.0.0" @@ -1634,13 +1929,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, ] [package.dependencies] @@ -1648,13 +1943,13 @@ referencing = ">=0.31.0" [[package]] name = "julep" -version = "1.14.0" +version = "1.43.1" description = "The official Python library for the julep API" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "julep-1.14.0-py3-none-any.whl", hash = "sha256:a2770e63e94bc3ee21e4408d5b9e1595b0cb70cf20abecf3088f66b649616ece"}, - {file = "julep-1.14.0.tar.gz", hash = "sha256:22446414c66983d7496f5857a8b57e76394d3c2ce204bfee3cf5c6c68c4663a5"}, + {file = "julep-1.43.1-py3-none-any.whl", hash = "sha256:a9a5d8c92b77d1983bd0b4edf8c493e0db1db65713b78ca87f247fc8e23bd785"}, + {file = "julep-1.43.1.tar.gz", hash = "sha256:24bd12445afd6e5a55612d6172f9776262a21c044aaeebd11f943f1b9a52b59a"}, ] [package.dependencies] @@ -1662,6 +1957,8 @@ anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" pydantic = ">=1.9.0,<3" +python-dotenv = ">=1.0,<1.1" +ruamel-yaml = ">=0.18.6,<0.19" sniffio = "*" typing-extensions = ">=4.7,<5" @@ -1803,13 +2100,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.2.5" +version = "4.3.1" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.2.5-py3-none-any.whl", hash = "sha256:73b6e0775d41a9fee7ee756c80f58a6bed4040869ccc21411dc559818874d321"}, - {file = "jupyterlab-4.2.5.tar.gz", hash = "sha256:ae7f3a1b8cb88b4f55009ce79fa7c06f99d70cd63601ee4aa91815d054f46f75"}, + {file = "jupyterlab-4.3.1-py3-none-any.whl", hash = "sha256:2d9a1c305bc748e277819a17a5d5e22452e533e835f4237b2f30f3b0e491e01f"}, + {file = "jupyterlab-4.3.1.tar.gz", hash = "sha256:a4a338327556443521731d82f2a6ccf926df478914ca029616621704d47c3c65"}, ] [package.dependencies] @@ -1828,9 +2125,9 @@ tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.6.9)"] +docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.1.0)", "sphinx-copybutton"] +docs-screenshots = ["altair (==5.4.1)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.2.post3)", "matplotlib (==3.9.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.14.1)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] @@ -1881,67 +2178,180 @@ files = [ {file = "jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed"}, ] +[[package]] +name = "langchain-core" +version = "0.3.21" +description = "Building applications with LLMs through composability" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "langchain_core-0.3.21-py3-none-any.whl", hash = "sha256:7e723dff80946a1198976c6876fea8326dc82566ef9bcb5f8d9188f738733665"}, + {file = "langchain_core-0.3.21.tar.gz", hash = "sha256:561b52b258ffa50a9fb11d7a1940ebfd915654d1ec95b35e81dfd5ee84143411"}, +] + +[package.dependencies] +jsonpatch = ">=1.33,<2.0" +langsmith = ">=0.1.125,<0.2.0" +packaging = ">=23.2,<25" +pydantic = [ + {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, + {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, +] +PyYAML = ">=5.3" +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0" +typing-extensions = ">=4.7" + +[[package]] +name = "langcodes" +version = "3.5.0" +description = "Tools for labeling human languages with IETF language tags" +optional = false +python-versions = ">=3.9" +files = [ + {file = "langcodes-3.5.0-py3-none-any.whl", hash = "sha256:853c69d1a35e0e13da2f427bb68fb2fa4a8f4fb899e0c62ad8df8d073dcfed33"}, + {file = "langcodes-3.5.0.tar.gz", hash = "sha256:1eef8168d07e51e131a2497ffecad4b663f6208e7c3ae3b8dc15c51734a6f801"}, +] + +[package.dependencies] +language-data = ">=1.2" + +[package.extras] +build = ["build", "twine"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "langsmith" +version = "0.1.147" +description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"}, + {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"}, +] + +[package.dependencies] +httpx = ">=0.23.0,<1" +orjson = {version = ">=3.9.14,<4.0.0", markers = "platform_python_implementation != \"PyPy\""} +pydantic = [ + {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, + {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, +] +requests = ">=2,<3" +requests-toolbelt = ">=1.0.0,<2.0.0" + +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + +[[package]] +name = "language-data" +version = "1.3.0" +description = "Supplementary data about languages used by the langcodes module" +optional = false +python-versions = "*" +files = [ + {file = "language_data-1.3.0-py3-none-any.whl", hash = "sha256:e2ee943551b5ae5f89cd0e801d1fc3835bb0ef5b7e9c3a4e8e17b2b214548fbf"}, + {file = "language_data-1.3.0.tar.gz", hash = "sha256:7600ef8aa39555145d06c89f0c324bf7dab834ea0b0a439d8243762e3ebad7ec"}, +] + +[package.dependencies] +marisa-trie = ">=1.1.0" + +[package.extras] +build = ["build", "twine"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "larch-pickle" +version = "1.4.3" +description = "A faster python pickle replacement" +optional = false +python-versions = "*" +files = [ + {file = "larch-pickle-1.4.3.tar.gz", hash = "sha256:560575f0061bf4e598adfc10f2b84b0ae3b61f8c27734fdd62775a5659c3a611"}, + {file = "larch_pickle-1.4.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:d9de944a602fb8621e0ad841641924353dbf5594bfecb2c262742b3b26f2a83b"}, + {file = "larch_pickle-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:462be1dcaaaa88fef0d473d0ac8ae431f0e172778c44b080dbd68233731c3d51"}, + {file = "larch_pickle-1.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:01bafdb4322c3d7cf111b9df5f4cbcdbfb5ffdd6ad3eecd4a40c57fedff52d5b"}, + {file = "larch_pickle-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:0af8866c6d2d6b7e211ea7ac7d202fe2fc8bfa15774dfec686cb1c75f9e0e2c6"}, + {file = "larch_pickle-1.4.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:43aa630e586c564605325effe87fd3f38692b71cf3f960efe48c2df4f7967a06"}, + {file = "larch_pickle-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:18c74fde0bdd0382598745633b6a230a28707bbd382e695fae1518324d926b8a"}, +] + [[package]] name = "libcst" -version = "1.4.0" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." +version = "1.5.1" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." optional = false python-versions = ">=3.9" files = [ - {file = "libcst-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:279b54568ea1f25add50ea4ba3d76d4f5835500c82f24d54daae4c5095b986aa"}, - {file = "libcst-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3401dae41fe24565387a65baee3887e31a44e3e58066b0250bc3f3ccf85b1b5a"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1989fa12d3cd79118ebd29ebe2a6976d23d509b1a4226bc3d66fcb7cb50bd5d"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addc6d585141a7677591868886f6bda0577529401a59d210aa8112114340e129"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d71001cb25e94cfe8c3d997095741a8c4aa7a6d234c0f972bc42818c88dfaf"}, - {file = "libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6227562fc5c9c1efd15dfe90b0971ae254461b8b6b23c1b617139b6003de1c1"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3399e6c95df89921511b44d8c5bf6a75bcbc2d51f1f6429763609ba005c10f6b"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48601e3e590e2d6a7ab8c019cf3937c70511a78d778ab3333764531253acdb33"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42797309bb725f0f000510d5463175ccd7155395f09b5e7723971b0007a976d"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb4e42ea107a37bff7f9fdbee9532d39f9ea77b89caa5c5112b37057b12e0838"}, - {file = "libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7ece51d935bc9bf60b528473d2e5cc67cbb88e2f8146297e40ee2c7d80be6f13"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:81653dea1cdfa4c6520a7c5ffb95fa4d220cbd242e446c7a06d42d8636bfcbba"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6abce0e66bba2babfadc20530fd3688f672d565674336595b4623cd800b91ef"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da9d7dc83801aba3b8d911f82dc1a375db0d508318bad79d9fb245374afe068"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c54aa66c86d8ece9c93156a2cf5ca512b0dce40142fe9e072c86af2bf892411"}, - {file = "libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8ecdba8934632b4dadacb666cd3816627a6ead831b806336972ccc4ba7ca0e9"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e54c777b8d27339b70f304d16fc8bc8674ef1bd34ed05ea874bf4921eb5a313"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:061d6855ef30efe38b8a292b7e5d57c8e820e71fc9ec9846678b60a934b53bbb"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0abf627ee14903d05d0ad9b2c6865f1b21eb4081e2c7bea1033f85db2b8bae"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d024f44059a853b4b852cfc04fec33e346659d851371e46fc8e7c19de24d3da9"}, - {file = "libcst-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c6a8faab9da48c5b371557d0999b4ca51f4f2cbd37ee8c2c4df0ac01c781465"}, - {file = "libcst-1.4.0.tar.gz", hash = "sha256:449e0b16604f054fa7f27c3ffe86ea7ef6c409836fe68fe4e752a1894175db00"}, + {file = "libcst-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab83633e61ee91df575a3838b1e73c371f19d4916bf1816554933235553d41ea"}, + {file = "libcst-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b58a49895d95ec1fd34fad041a142d98edf9b51fcaf632337c13befeb4d51c7c"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9ec764aa781ef35ab96b693569ac3dced16df9feb40ee6c274d13e86a1472e"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99bbffd8596d192bc0e844a4cf3c4fc696979d4e20ab1c0774a01768a59b47ed"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec6ee607cfe4cc4cc93e56e0188fdb9e50399d61a1262d58229752946f288f5e"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72132756f985a19ef64d702a821099d4afc3544974662772b44cbc55b7279727"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:40b75bf2d70fc0bc26b1fa73e61bdc46fef59f5c71aedf16128e7c33db8d5e40"}, + {file = "libcst-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:56c944acaa781b8e586df3019374f5cf117054d7fc98f85be1ba84fe810005dc"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db7711a762b0327b581be5a963908fecd74412bdda34db34553faa521563c22d"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa524bd012aaae1f485fd44490ef5abf708b14d2addc0f06b28de3e4585c4b9e"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffb8135c09e41e8cf710b152c33e9b7f1d0d0b9f242bae0c502eb082fdb1fb"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76a8ac7a84f9b6f678a668bff85b360e0a93fa8d7f25a74a206a28110734bb2a"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89c808bdb5fa9ca02df41dd234cbb0e9de0d2e0c029c7063d5435a9f6781cc10"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40fbbaa8b839bfbfa5b300623ca2b6b0768b58bbc31b341afbc99110c9bee232"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c7021e3904d8d088c369afc3fe17c279883e583415ef07edacadba76cfbecd27"}, + {file = "libcst-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f053a5deb6a214972dbe9fa26ecd8255edb903de084a3d7715bf9e9da8821c50"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:666813950b8637af0c0e96b1ca46f5d5f183d2fe50bbac2186f5b283a99f3529"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b58b36022ae77a5a00002854043ae95c03e92f6062ad08473eff326f32efa0"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb13d7c598fe9a798a1d22eae56ab3d3d599b38b83436039bd6ae229fc854d7"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5987daff8389b0df60b5c20499ff4fb73fc03cb3ae1f6a746eefd204ed08df85"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f3d2f32ee081bad3394546b0b9ac5e31686d3b5cfe4892d716d2ba65f9ec08"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ff21005c33b634957a98db438e882522febf1cacc62fa716f29e163a3f5871a"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:15697ea9f1edbb9a263364d966c72abda07195d1c1a6838eb79af057f1040770"}, + {file = "libcst-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:cedd4c8336e01c51913113fbf5566b8f61a86d90f3d5cc5b1cb5049575622c5f"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:06a9b4c9b76da4a7399e6f1f3a325196fb5febd3ea59fac1f68e2116f3517cd8"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:940ec4c8db4c2d620a7268d6c83e64ff646e4afd74ae5183d0f0ef3b80e05be0"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbccb016b1ac6d892344300dcccc8a16887b71bb7f875ba56c0ed6c1a7ade8be"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c615af2117320e9a218083c83ec61227d3547e38a0de80329376971765f27a9e"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02b38fa4d9f13e79fe69e9b5407b9e173557bcfb5960f7866cf4145af9c7ae09"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3334afe9e7270e175de01198f816b0dc78dda94d9d72152b61851c323e4e741e"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26c804fa8091747128579013df0b5f8e6b0c7904d9c4ee83841f136f53e18684"}, + {file = "libcst-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:b5a0d3c632aa2b21c5fa145e4e8dbf86f45c9b37a64c0b7221a5a45caf58915a"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1cc7393aaac733e963f0ee00466d059db74a38e15fc7e6a46dddd128c5be8d08"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bbaf5755be50fa9b35a3d553d1e62293fbb2ee5ce2c16c7e7ffeb2746af1ab88"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e397f5b6c0fc271acea44579f154b0f3ab36011050f6db75ab00cef47441946"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1947790a4fd7d96bcc200a6ecaa528045fcb26a34a24030d5859c7983662289e"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:697eabe9f5ffc40f76d6d02e693274e0a382826d0cf8183bd44e7407dfb0ab90"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dc06b7c60d086ef1832aebfd31b64c3c8a645adf0c5638d6243e5838f6a9356e"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19e39cfef4316599ca20d1c821490aeb783b52e8a8543a824972a525322a85d0"}, + {file = "libcst-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:01e01c04f0641188160d3b99c6526436e93a3fbf9783dba970f9885a77ec9b38"}, + {file = "libcst-1.5.1.tar.gz", hash = "sha256:71cb294db84df9e410208009c732628e920111683c2f2b2e0c5b71b98464f365"}, ] [package.dependencies] pyyaml = ">=5.2" [package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.6)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.6.0)", "usort (==1.0.8.post1)"] +dev = ["Sphinx (>=5.1.1)", "black (==24.8.0)", "build (>=0.10.0)", "coverage[toml] (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.1.1)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=1.7.0,<1.8)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.7.3)", "usort (==1.0.8.post1)"] [[package]] name = "litellm" -version = "1.48.17" +version = "1.52.10" description = "Library to easily interface with LLM API providers" optional = false -python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" -files = [ - {file = "litellm-1.48.17-py3-none-any.whl", hash = "sha256:f3c25b8bcdbe2c65fbe492a674574461c200040e9e633073108c9517630469b6"}, - {file = "litellm-1.48.17.tar.gz", hash = "sha256:5b5039b39c4a9f748af8253895eec76b3458960533d1e038d1aec409d550ee37"}, -] +python-versions = ">=3.8.1,<4.0, !=3.9.7" +files = [] +develop = false [package.dependencies] aiohttp = "*" click = "*" importlib-metadata = ">=6.8.0" -jinja2 = ">=3.1.2,<4.0.0" -jsonschema = ">=4.22.0,<5.0.0" -openai = ">=1.51.0" -pydantic = ">=2.0.0,<3.0.0" +jinja2 = "^3.1.2" +jsonschema = "^4.22.0" +openai = ">=1.54.0" +pydantic = "^2.0.0" python-dotenv = ">=0.2.0" -requests = ">=2.31.0,<3.0.0" +requests = "^2.31.0" tiktoken = ">=0.7.0" tokenizers = "*" @@ -1949,6 +2359,12 @@ tokenizers = "*" extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] +[package.source] +type = "git" +url = "https://github.com/julep-ai/litellm.git" +reference = "fix_anthropic_tool_image_content" +resolved_reference = "45a0d77a1446e0eb3feeebbf717ef43bfd0e7f7d" + [[package]] name = "lz4" version = "4.3.3" @@ -1999,6 +2415,97 @@ docs = ["sphinx (>=1.6.0)", "sphinx-bootstrap-theme"] flake8 = ["flake8"] tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"] +[[package]] +name = "marisa-trie" +version = "1.2.1" +description = "Static memory-efficient and fast Trie-like structures for Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "marisa_trie-1.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2eb41d2f9114d8b7bd66772c237111e00d2bae2260824560eaa0a1e291ce9e8"}, + {file = "marisa_trie-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e956e6a46f604b17d570901e66f5214fb6f658c21e5e7665deace236793cef6"}, + {file = "marisa_trie-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd45142501300e7538b2e544905580918b67b1c82abed1275fe4c682c95635fa"}, + {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8443d116c612cfd1961fbf76769faf0561a46d8e317315dd13f9d9639ad500c"}, + {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:875a6248e60fbb48d947b574ffa4170f34981f9e579bde960d0f9a49ea393ecc"}, + {file = "marisa_trie-1.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:746a7c60a17fccd3cfcfd4326926f02ea4fcdfc25d513411a0c4fc8e4a1ca51f"}, + {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e70869737cc0e5bd903f620667da6c330d6737048d1f44db792a6af68a1d35be"}, + {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06b099dd743676dbcd8abd8465ceac8f6d97d8bfaabe2c83b965495523b4cef2"}, + {file = "marisa_trie-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2a82eb21afdaf22b50d9b996472305c05ca67fc4ff5a026a220320c9c961db6"}, + {file = "marisa_trie-1.2.1-cp310-cp310-win32.whl", hash = "sha256:8951e7ce5d3167fbd085703b4cbb3f47948ed66826bef9a2173c379508776cf5"}, + {file = "marisa_trie-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:5685a14b3099b1422c4f59fa38b0bf4b5342ee6cc38ae57df9666a0b28eeaad3"}, + {file = "marisa_trie-1.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed3fb4ed7f2084597e862bcd56c56c5529e773729a426c083238682dba540e98"}, + {file = "marisa_trie-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe69fb9ffb2767746181f7b3b29bbd3454d1d24717b5958e030494f3d3cddf3"}, + {file = "marisa_trie-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4728ed3ae372d1ea2cdbd5eaa27b8f20a10e415d1f9d153314831e67d963f281"}, + {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cf4f25cf895692b232f49aa5397af6aba78bb679fb917a05fce8d3cb1ee446d"}, + {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cca7f96236ffdbf49be4b2e42c132e3df05968ac424544034767650913524de"}, + {file = "marisa_trie-1.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7eb20bf0e8b55a58d2a9b518aabc4c18278787bdba476c551dd1c1ed109e509"}, + {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b1ec93f0d1ee6d7ab680a6d8ea1a08bf264636358e92692072170032dda652ba"}, + {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e2699255d7ac610dee26d4ae7bda5951d05c7d9123a22e1f7c6a6f1964e0a4e4"}, + {file = "marisa_trie-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c484410911182457a8a1a0249d0c09c01e2071b78a0a8538cd5f7fa45589b13a"}, + {file = "marisa_trie-1.2.1-cp311-cp311-win32.whl", hash = "sha256:ad548117744b2bcf0e3d97374608be0a92d18c2af13d98b728d37cd06248e571"}, + {file = "marisa_trie-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:436f62d27714970b9cdd3b3c41bdad046f260e62ebb0daa38125ef70536fc73b"}, + {file = "marisa_trie-1.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:638506eacf20ca503fff72221a7e66a6eadbf28d6a4a6f949fcf5b1701bb05ec"}, + {file = "marisa_trie-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de1665eaafefa48a308e4753786519888021740501a15461c77bdfd57638e6b4"}, + {file = "marisa_trie-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f713af9b8aa66a34cd3a78c7d150a560a75734713abe818a69021fd269e927fa"}, + {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2a7d00f53f4945320b551bccb826b3fb26948bde1a10d50bb9802fabb611b10"}, + {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98042040d1d6085792e8d0f74004fc0f5f9ca6091c298f593dd81a22a4643854"}, + {file = "marisa_trie-1.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6532615111eec2c79e711965ece0bc95adac1ff547a7fff5ffca525463116deb"}, + {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:20948e40ab2038e62b7000ca6b4a913bc16c91a2c2e6da501bd1f917eeb28d51"}, + {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66b23e5b35dd547f85bf98db7c749bc0ffc57916ade2534a6bbc32db9a4abc44"}, + {file = "marisa_trie-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6704adf0247d2dda42e876b793be40775dff46624309ad99bc7537098bee106d"}, + {file = "marisa_trie-1.2.1-cp312-cp312-win32.whl", hash = "sha256:3ad356442c2fea4c2a6f514738ddf213d23930f942299a2b2c05df464a00848a"}, + {file = "marisa_trie-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2806f75817392cedcacb24ac5d80b0350dde8d3861d67d045c1d9b109764114"}, + {file = "marisa_trie-1.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b5ea16e69bfda0ac028c921b58de1a4aaf83d43934892977368579cd3c0a2554"}, + {file = "marisa_trie-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f627f4e41be710b6cb6ed54b0128b229ac9d50e2054d9cde3af0fef277c23cf"}, + {file = "marisa_trie-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e649f3dc8ab5476732094f2828cc90cac3be7c79bc0c8318b6fda0c1d248db4"}, + {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46e528ee71808c961baf8c3ce1c46a8337ec7a96cc55389d11baafe5b632f8e9"}, + {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36aa4401a1180615f74d575571a6550081d84fc6461e9aefc0bb7b2427af098e"}, + {file = "marisa_trie-1.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce59bcd2cda9bb52b0e90cc7f36413cd86c3d0ce7224143447424aafb9f4aa48"}, + {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f4cd800704a5fc57e53c39c3a6b0c9b1519ebdbcb644ede3ee67a06eb542697d"}, + {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2428b495003c189695fb91ceeb499f9fcced3a2dce853e17fa475519433c67ff"}, + {file = "marisa_trie-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:735c363d9aaac82eaf516a28f7c6b95084c2e176d8231c87328dc80e112a9afa"}, + {file = "marisa_trie-1.2.1-cp313-cp313-win32.whl", hash = "sha256:eba6ca45500ca1a042466a0684aacc9838e7f20fe2605521ee19f2853062798f"}, + {file = "marisa_trie-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:aa7cd17e1c690ce96c538b2f4aae003d9a498e65067dd433c52dd069009951d4"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5e43891a37b0d7f618819fea14bd951289a0a8e3dd0da50c596139ca83ebb9b1"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6946100a43f933fad6bc458c502a59926d80b321d5ac1ed2ff9c56605360496f"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4177dc0bd1374e82be9b2ba4d0c2733b0a85b9d154ceeea83a5bee8c1e62fbf"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f35c2603a6be168088ed1db6ad1704b078aa8f39974c60888fbbced95dcadad4"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d659fda873d8dcb2c14c2c331de1dee21f5a902d7f2de7978b62c6431a8850ef"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:b0ef26733d3c836be79e812071e1a431ce1f807955a27a981ebb7993d95f842b"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:536ea19ce6a2ce61c57fed4123ecd10d18d77a0db45cd2741afff2b8b68f15b3"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-win32.whl", hash = "sha256:0ee6cf6a16d9c3d1c94e21c8e63c93d8b34bede170ca4e937e16e1c0700d399f"}, + {file = "marisa_trie-1.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7e7b1786e852e014d03e5f32dbd991f9a9eb223dd3fa9a2564108b807e4b7e1c"}, + {file = "marisa_trie-1.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:952af3a5859c3b20b15a00748c36e9eb8316eb2c70bd353ae1646da216322908"}, + {file = "marisa_trie-1.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24a81aa7566e4ec96fc4d934581fe26d62eac47fc02b35fa443a0bb718b471e8"}, + {file = "marisa_trie-1.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9c9b32b14651a6dcf9e8857d2df5d29d322a1ea8c0be5c8ffb88f9841c4ec62b"}, + {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ac170d20b97beb75059ba65d1ccad6b434d777c8992ab41ffabdade3b06dd74"}, + {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da4e4facb79614cc4653cfd859f398e4db4ca9ab26270ff12610e50ed7f1f6c6"}, + {file = "marisa_trie-1.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25688f34cac3bec01b4f655ffdd6c599a01f0bd596b4a79cf56c6f01a7df3560"}, + {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1db3213b451bf058d558f6e619bceff09d1d130214448a207c55e1526e2773a1"}, + {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5648c6dcc5dc9200297fb779b1663b8a4467bda034a3c69bd9c32d8afb33b1d"}, + {file = "marisa_trie-1.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5bd39a4e1cc839a88acca2889d17ebc3f202a5039cd6059a13148ce75c8a6244"}, + {file = "marisa_trie-1.2.1-cp38-cp38-win32.whl", hash = "sha256:594f98491a96c7f1ffe13ce292cef1b4e63c028f0707effdea0f113364c1ae6c"}, + {file = "marisa_trie-1.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:5fe5a286f997848a410eebe1c28657506adaeb405220ee1e16cfcfd10deb37f2"}, + {file = "marisa_trie-1.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c0fe2ace0cb1806badbd1c551a8ec2f8d4cf97bf044313c082ef1acfe631ddca"}, + {file = "marisa_trie-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67f0c2ec82c20a02c16fc9ba81dee2586ef20270127c470cb1054767aa8ba310"}, + {file = "marisa_trie-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3c98613180cf1730e221933ff74b454008161b1a82597e41054127719964188"}, + {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:429858a0452a7bedcf67bc7bb34383d00f666c980cb75a31bcd31285fbdd4403"}, + {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2eacb84446543082ec50f2fb563f1a94c96804d4057b7da8ed815958d0cdfbe"}, + {file = "marisa_trie-1.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:852d7bcf14b0c63404de26e7c4c8d5d65ecaeca935e93794331bc4e2f213660b"}, + {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e58788004adda24c401d1751331618ed20c507ffc23bfd28d7c0661a1cf0ad16"}, + {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aefe0973cc4698e0907289dc0517ab0c7cdb13d588201932ff567d08a50b0e2e"}, + {file = "marisa_trie-1.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6c50c861faad0a5c091bd763e0729f958c316e678dfa065d3984fbb9e4eacbcd"}, + {file = "marisa_trie-1.2.1-cp39-cp39-win32.whl", hash = "sha256:b1ce340da608530500ab4f963f12d6bfc8d8680900919a60dbdc9b78c02060a4"}, + {file = "marisa_trie-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:ce37d8ca462bb64cc13f529b9ed92f7b21fe8d1f1679b62e29f9cb7d0e888b49"}, + {file = "marisa_trie-1.2.1.tar.gz", hash = "sha256:3a27c408e2aefc03e0f1d25b2ff2afb85aac3568f6fa2ae2a53b57a2e87ce29d"}, +] + +[package.dependencies] +setuptools = "*" + +[package.extras] +test = ["hypothesis", "pytest", "readme-renderer"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2025,91 +2532,92 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] name = "marshmallow" -version = "3.22.0" +version = "3.23.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, - {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, + {file = "marshmallow-3.23.1-py3-none-any.whl", hash = "sha256:fece2eb2c941180ea1b7fcbd4a83c51bfdd50093fdd3ad2585ee5e1df2508491"}, + {file = "marshmallow-3.23.1.tar.gz", hash = "sha256:3a8dfda6edd8dcdbf216c0ede1d1e78d230a6dc9c5a088f58c4083b974a0d468"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "simplejson"] [[package]] name = "matplotlib-inline" @@ -2147,6 +2655,79 @@ files = [ {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, ] +[[package]] +name = "msgpack" +version = "1.1.0" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, +] + [[package]] name = "msgspec" version = "0.18.6" @@ -2300,6 +2881,41 @@ files = [ {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] +[[package]] +name = "murmurhash" +version = "1.0.11" +description = "Cython bindings for MurmurHash" +optional = false +python-versions = ">=3.6" +files = [ + {file = "murmurhash-1.0.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a73cf9f55c8218d5aa47b3b6dac28fa2e1730bbca0874e7eabe5e1a6024780c5"}, + {file = "murmurhash-1.0.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48716859a12596024d9adecf399e356c3c5c38ba2eb0d8270bd6655c05a0af28"}, + {file = "murmurhash-1.0.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1967ccc893c80798a420c5c3829ea9755d0b4a4972b0bf6e5c34d1117f5d0222"}, + {file = "murmurhash-1.0.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:904c4d6550c640e0f640b6357ecaa13406e6d925e55fbb4ac9e1f27ff25bee3c"}, + {file = "murmurhash-1.0.11-cp310-cp310-win_amd64.whl", hash = "sha256:4c24f1c96e8ce720ac85058c37e6e775be6017f0966abff2863733d91368e03e"}, + {file = "murmurhash-1.0.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53ed86ce0bef2475af9314f732ca66456e7b00abb1d1a6c29c432e5f0f49bad5"}, + {file = "murmurhash-1.0.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51e7c61f59e0ee1c465c841f530ef6373a98dc028059048fc0c857dfd5d57b1c"}, + {file = "murmurhash-1.0.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b9a5109e29d43c79bfdca8dbad9bee7190846a88ec6d4135754727fb49a64e5"}, + {file = "murmurhash-1.0.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12845ad43a2e54734b52f58e8d228eacd03803d368b689b3868a0bdec4c10da1"}, + {file = "murmurhash-1.0.11-cp311-cp311-win_amd64.whl", hash = "sha256:e3d0bdbffd82924725cd6549b03ee11997a2c58253f0fdda571a5fedacc894a1"}, + {file = "murmurhash-1.0.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:185b2cd20b81fa876eaa2249faafd0b7b3d0c54ef04714e38135d9f482cf6ce9"}, + {file = "murmurhash-1.0.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd3083c6d977c2bc1e2f35ff999c39de43de09fd588f780243ec78debb316406"}, + {file = "murmurhash-1.0.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49a3cf4d26f7213d0f4a6c2c49496cbe9f78b30d56b1c3b17fbc74676372ea3f"}, + {file = "murmurhash-1.0.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1bdb3c3fe32d93f7c461f11e6b2f7bbe64b3d70f56e48052490435853ed5c91"}, + {file = "murmurhash-1.0.11-cp312-cp312-win_amd64.whl", hash = "sha256:0b507dd8ea10f3e5204b397ea9917a3a5f11756859d91406a8f485f18a411bdf"}, + {file = "murmurhash-1.0.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:036aea55d160d65698888a903fd2a19c4258be711f7bf2ab1b6cebdf41e09e09"}, + {file = "murmurhash-1.0.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f4b991b5bd88f5d57550a6328f8adb2f16656781e9eade9c16e55b41f6fab7"}, + {file = "murmurhash-1.0.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5527ec305236a2ef404a38e0e57b1dc886a431e2032acf4c7ce3b17382c49ef"}, + {file = "murmurhash-1.0.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b26cf1be87c13fb242b9c252f11a25da71056c8fb5f22623e455129cce99592a"}, + {file = "murmurhash-1.0.11-cp313-cp313-win_amd64.whl", hash = "sha256:24aba80a793bf371de70fffffc1f16c06810e4d8b90125b5bb762aabda3174d1"}, + {file = "murmurhash-1.0.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:234cc9719a5df1bffe174664b84b8381f66016a1f094d43db3fb8ffca1d72207"}, + {file = "murmurhash-1.0.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faf1db780cfca0a021ce32542ac750d24b9b3e81e2a4a6fcb78efcc8ec611813"}, + {file = "murmurhash-1.0.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1f7f7c8bce5fa1c50c6214421af27eb0bbb07cc55c4a35efa5735ceaf1a6a1c"}, + {file = "murmurhash-1.0.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b8d8fad28cf7d9661486f8e3d48e4215db69f5f9b091e78edcccf2c46459846a"}, + {file = "murmurhash-1.0.11-cp39-cp39-win_amd64.whl", hash = "sha256:6ae5fc4f59be8eebcb8d24ffee49f32ee4eccdc004060848834eb2540ee3a056"}, + {file = "murmurhash-1.0.11.tar.gz", hash = "sha256:87ff68a255e54e7648d0729ff4130f43f7f38f03288a376e567934e16db93767"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -2313,13 +2929,13 @@ files = [ [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -2330,7 +2946,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -2404,48 +3020,51 @@ files = [ [[package]] name = "networkx" -version = "3.1" +version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, - {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, + {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, + {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, ] [package.extras] -default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] -developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] -doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] -test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] +default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "ninja" -version = "1.11.1.1" +version = "1.11.1.2" description = "Ninja is a small build system with a focus on speed" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885"}, - {file = "ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a"}, - {file = "ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c"}, + {file = "ninja-1.11.1.2-py3-none-macosx_10_9_universal2.whl", hash = "sha256:1cfbb845095ea09da8c089375a8f999e75f4817d01506297c66181b533175647"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ab4068ff7ff1f895485ad604116165b05d6810c802170a7f22c09dd678d5587d"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:33d258809c8eda81f9d80e18a081a6eef3215e5fd1ba8902400d786641994e89"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed25892c16e49e66383a8db6a67a9f33b41230fc485426094d7da51e2255ec2b"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:232767144401847db62e8392047866698bb3678158a1ae4400a97111110e90f2"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9200247cf4c1643a67d079836b8dd31a362e34e618b50b5e3a5c0d0171efc442"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c9c36f6e6f8946c7271b0ed14d98fc3ea467a0c0954fb73f5f656c42667d943"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:3e815e4147832b17ec38417efcb31df51671ae273f083409304c7cc32a14dd1a"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ecf3df324b56fdfb0872990a71e706efdae286e010310816c72b6bf24431711b"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:cb6b476eb4e84c0efcfd3ab04f660dedce8adb854b56b043639312f3af176df6"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:508fb93395a5c82a4d99d30fce0cbaf5cb2bd33e5c1dc9faaa080e199802dbc9"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:52af7f45750c5c288d566fd0c927ed9bb0d8f2e50803709f582a42bcc4ec167b"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99fc4b87299242e10d7edd1c7737fdfb1269019e32f9f4267630887f6183a49e"}, + {file = "ninja-1.11.1.2-py3-none-win32.whl", hash = "sha256:949e23cb2e79a33ea37d23a07d26846d2e75464e8e6940f8751fe964bc141dfa"}, + {file = "ninja-1.11.1.2-py3-none-win_amd64.whl", hash = "sha256:0bca4179119426a3c3c9d5661c3b244d68781064e50907a1e066bc55edc18e06"}, + {file = "ninja-1.11.1.2-py3-none-win_arm64.whl", hash = "sha256:ee7b1924c28e6cab5b866f7b229f07777d25d8cfccbbedf3da5ffb4f72f57877"}, + {file = "ninja-1.11.1.2.tar.gz", hash = "sha256:4fbd07b2b4232543726abafdd350453a2fabef4527664ca0e491c578aee5f857"}, ] [package.extras] -test = ["codecov (>=2.0.5)", "coverage (>=4.2)", "flake8 (>=3.0.4)", "pytest (>=4.5.0)", "pytest-cov (>=2.7.1)", "pytest-runner (>=5.1)", "pytest-virtualenv (>=1.7.0)", "virtualenv (>=15.0.3)"] +test = ["coverage (>=4.2)", "importlib_metadata (>=2.0)", "pytest (>=6.0)", "pytest-cov (>=3)"] [[package]] name = "notebook-shim" @@ -2466,75 +3085,67 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "numpy" -version = "2.1.2" +version = "2.0.2" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.10" +python-versions = ">=3.9" files = [ - {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, - {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, - {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, - {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, - {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, - {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, - {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, - {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, - {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, - {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, + {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, + {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, + {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, + {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, + {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, + {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, + {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, + {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, + {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, ] [[package]] name = "openai" -version = "1.51.0" +version = "1.55.3" description = "The official Python library for the openai API" optional = false -python-versions = ">=3.7.1" +python-versions = ">=3.8" files = [ - {file = "openai-1.51.0-py3-none-any.whl", hash = "sha256:d9affafb7e51e5a27dce78589d4964ce4d6f6d560307265933a94b2e3f3c5d2c"}, - {file = "openai-1.51.0.tar.gz", hash = "sha256:8dc4f9d75ccdd5466fc8c99a952186eddceb9fd6ba694044773f3736a847149d"}, + {file = "openai-1.55.3-py3-none-any.whl", hash = "sha256:2a235d0e1e312cd982f561b18c27692e253852f4e5fb6ccf08cb13540a9bdaa1"}, + {file = "openai-1.55.3.tar.gz", hash = "sha256:547e85b94535469f137a779d8770c8c5adebd507c2cc6340ca401a7c4d5d16f0"}, ] [package.dependencies] @@ -2550,6 +3161,90 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +[[package]] +name = "orjson" +version = "3.10.12" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, +] + [[package]] name = "overrides" version = "7.7.0" @@ -2563,13 +3258,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -2776,6 +3471,52 @@ files = [ {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, ] +[[package]] +name = "preshed" +version = "3.0.9" +description = "Cython hash table that trusts the keys are pre-hashed" +optional = false +python-versions = ">=3.6" +files = [ + {file = "preshed-3.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f96ef4caf9847b2bb9868574dcbe2496f974e41c2b83d6621c24fb4c3fc57e3"}, + {file = "preshed-3.0.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a61302cf8bd30568631adcdaf9e6b21d40491bd89ba8ebf67324f98b6c2a2c05"}, + {file = "preshed-3.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99499e8a58f58949d3f591295a97bca4e197066049c96f5d34944dd21a497193"}, + {file = "preshed-3.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea6b6566997dc3acd8c6ee11a89539ac85c77275b4dcefb2dc746d11053a5af8"}, + {file = "preshed-3.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:bfd523085a84b1338ff18f61538e1cfcdedc4b9e76002589a301c364d19a2e36"}, + {file = "preshed-3.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7c2364da27f2875524ce1ca754dc071515a9ad26eb5def4c7e69129a13c9a59"}, + {file = "preshed-3.0.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:182138033c0730c683a6d97e567ceb8a3e83f3bff5704f300d582238dbd384b3"}, + {file = "preshed-3.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:345a10be3b86bcc6c0591d343a6dc2bfd86aa6838c30ced4256dfcfa836c3a64"}, + {file = "preshed-3.0.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51d0192274aa061699b284f9fd08416065348edbafd64840c3889617ee1609de"}, + {file = "preshed-3.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:96b857d7a62cbccc3845ac8c41fd23addf052821be4eb987f2eb0da3d8745aa1"}, + {file = "preshed-3.0.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4fe6720012c62e6d550d6a5c1c7ad88cacef8388d186dad4bafea4140d9d198"}, + {file = "preshed-3.0.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e04f05758875be9751e483bd3c519c22b00d3b07f5a64441ec328bb9e3c03700"}, + {file = "preshed-3.0.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a55091d0e395f1fdb62ab43401bb9f8b46c7d7794d5b071813c29dc1ab22fd0"}, + {file = "preshed-3.0.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7de8f5138bcac7870424e09684dc3dd33c8e30e81b269f6c9ede3d8c7bb8e257"}, + {file = "preshed-3.0.9-cp312-cp312-win_amd64.whl", hash = "sha256:24229c77364628743bc29c5620c5d6607ed104f0e02ae31f8a030f99a78a5ceb"}, + {file = "preshed-3.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73b0f7ecc58095ebbc6ca26ec806008ef780190fe685ce471b550e7eef58dc2"}, + {file = "preshed-3.0.9-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb90ecd5bec71c21d95962db1a7922364d6db2abe284a8c4b196df8bbcc871e"}, + {file = "preshed-3.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:e304a0a8c9d625b70ba850c59d4e67082a6be9c16c4517b97850a17a282ebee6"}, + {file = "preshed-3.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1fa6d3d5529b08296ff9b7b4da1485c080311fd8744bbf3a86019ff88007b382"}, + {file = "preshed-3.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e5173809d85edd420fc79563b286b88b4049746b797845ba672cf9435c0e7"}, + {file = "preshed-3.0.9-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fe81eb21c7d99e8b9a802cc313b998c5f791bda592903c732b607f78a6b7dc4"}, + {file = "preshed-3.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:78590a4a952747c3766e605ce8b747741005bdb1a5aa691a18aae67b09ece0e6"}, + {file = "preshed-3.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3452b64d97ce630e200c415073040aa494ceec6b7038f7a2a3400cbd7858e952"}, + {file = "preshed-3.0.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ac970d97b905e9e817ec13d31befd5b07c9cfec046de73b551d11a6375834b79"}, + {file = "preshed-3.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eebaa96ece6641cd981491cba995b68c249e0b6877c84af74971eacf8990aa19"}, + {file = "preshed-3.0.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d473c5f6856e07a88d41fe00bb6c206ecf7b34c381d30de0b818ba2ebaf9406"}, + {file = "preshed-3.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:0de63a560f10107a3f0a9e252cc3183b8fdedcb5f81a86938fd9f1dcf8a64adf"}, + {file = "preshed-3.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3a9ad9f738084e048a7c94c90f40f727217387115b2c9a95c77f0ce943879fcd"}, + {file = "preshed-3.0.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a671dfa30b67baa09391faf90408b69c8a9a7f81cb9d83d16c39a182355fbfce"}, + {file = "preshed-3.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23906d114fc97c17c5f8433342495d7562e96ecfd871289c2bb2ed9a9df57c3f"}, + {file = "preshed-3.0.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778cf71f82cedd2719b256f3980d556d6fb56ec552334ba79b49d16e26e854a0"}, + {file = "preshed-3.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:a6e579439b329eb93f32219ff27cb358b55fbb52a4862c31a915a098c8a22ac2"}, + {file = "preshed-3.0.9.tar.gz", hash = "sha256:721863c5244ffcd2651ad0928951a2c7c77b102f4e11a251ad85d37ee7621660"}, +] + +[package.dependencies] +cymem = ">=2.0.2,<2.1.0" +murmurhash = ">=0.28.0,<1.1.0" + [[package]] name = "prometheus-client" version = "0.21.0" @@ -2790,6 +3531,21 @@ files = [ [package.extras] twisted = ["twisted"] +[[package]] +name = "prometheus-fastapi-instrumentator" +version = "7.0.0" +description = "Instrument your FastAPI with Prometheus metrics." +optional = false +python-versions = ">=3.8.1,<4.0.0" +files = [ + {file = "prometheus_fastapi_instrumentator-7.0.0-py3-none-any.whl", hash = "sha256:96030c43c776ee938a3dae58485ec24caed7e05bfc60fe067161e0d5b5757052"}, + {file = "prometheus_fastapi_instrumentator-7.0.0.tar.gz", hash = "sha256:5ba67c9212719f244ad7942d75ded80693b26331ee5dfc1e7571e4794a9ccbed"}, +] + +[package.dependencies] +prometheus-client = ">=0.8.0,<1.0.0" +starlette = ">=0.30.0,<1.0.0" + [[package]] name = "prompt-toolkit" version = "3.0.48" @@ -2804,54 +3560,162 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "protobuf" -version = "5.28.2" +version = "5.29.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d"}, - {file = "protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132"}, - {file = "protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f"}, - {file = "protobuf-5.28.2-cp38-cp38-win32.whl", hash = "sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0"}, - {file = "protobuf-5.28.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3"}, - {file = "protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36"}, - {file = "protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276"}, - {file = "protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece"}, - {file = "protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0"}, + {file = "protobuf-5.29.0-cp310-abi3-win32.whl", hash = "sha256:ea7fb379b257911c8c020688d455e8f74efd2f734b72dc1ea4b4d7e9fd1326f2"}, + {file = "protobuf-5.29.0-cp310-abi3-win_amd64.whl", hash = "sha256:34a90cf30c908f47f40ebea7811f743d360e202b6f10d40c02529ebd84afc069"}, + {file = "protobuf-5.29.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c931c61d0cc143a2e756b1e7f8197a508de5365efd40f83c907a9febf36e6b43"}, + {file = "protobuf-5.29.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:85286a47caf63b34fa92fdc1fd98b649a8895db595cfa746c5286eeae890a0b1"}, + {file = "protobuf-5.29.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:0d10091d6d03537c3f902279fcf11e95372bdd36a79556311da0487455791b20"}, + {file = "protobuf-5.29.0-cp38-cp38-win32.whl", hash = "sha256:0cd67a1e5c2d88930aa767f702773b2d054e29957432d7c6a18f8be02a07719a"}, + {file = "protobuf-5.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:e467f81fdd12ded9655cea3e9b83dc319d93b394ce810b556fb0f421d8613e86"}, + {file = "protobuf-5.29.0-cp39-cp39-win32.whl", hash = "sha256:17d128eebbd5d8aee80300aed7a43a48a25170af3337f6f1333d1fac2c6839ac"}, + {file = "protobuf-5.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:6c3009e22717c6cc9e6594bb11ef9f15f669b19957ad4087214d69e08a213368"}, + {file = "protobuf-5.29.0-py3-none-any.whl", hash = "sha256:88c4af76a73183e21061881360240c0cdd3c39d263b4e8fb570aaf83348d608f"}, + {file = "protobuf-5.29.0.tar.gz", hash = "sha256:445a0c02483869ed8513a585d80020d012c6dc60075f96fa0563a724987b1001"}, ] [[package]] name = "psutil" -version = "6.0.0" +version = "6.1.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, + {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, + {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, + {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, + {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, + {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, + {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, + {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "ptyprocess" @@ -2908,6 +3772,23 @@ client = ["requests"] embedded = ["cozo-embedded (==0.7.6)"] pandas = ["ipython", "pandas"] +[[package]] +name = "pycozo-async" +version = "0.7.6" +description = "" +optional = false +python-versions = "<4.0,>=3.10" +files = [ + {file = "pycozo_async-0.7.6-py3-none-any.whl", hash = "sha256:f4c798ccf1315bff16c705792ff566e4e32e0e002dab5eb98d9e7c959015167a"}, + {file = "pycozo_async-0.7.6.tar.gz", hash = "sha256:20c66aade516abe0ed273c5955807a4ebd753e17fecad14f4f05277213f08d28"}, +] + +[package.dependencies] +cozo-embedded = ">=0.7.6,<0.8.0" +httpx = ">=0.27.2,<0.28.0" +ipython = ">=8.29.0,<9.0.0" +pandas = ">=2.2.3,<3.0.0" + [[package]] name = "pycparser" version = "2.22" @@ -2921,20 +3802,20 @@ files = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"}, + {file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"}, ] [package.dependencies] annotated-types = ">=0.6.0" email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.23.4" -typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} +pydantic-core = "2.27.1" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -2942,100 +3823,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -3090,13 +3982,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" -version = "2.9.0" +version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, - {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, + {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, + {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, ] [package.extras] @@ -3107,13 +3999,13 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pyparsing" -version = "3.1.4" +version = "3.2.0" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.9" files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, + {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, + {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, ] [package.extras] @@ -3145,9 +4037,6 @@ files = [ {file = "python_box-7.2.0.tar.gz", hash = "sha256:551af20bdab3a60a2a21e3435120453c4ca32f7393787c3a5036e1d9fc6a0ede"}, ] -[package.dependencies] -toml = {version = "*", optional = true, markers = "extra == \"toml\""} - [package.extras] all = ["msgpack", "ruamel.yaml (>=0.17)", "toml"] msgpack = ["msgpack"] @@ -3198,27 +4087,21 @@ files = [ [[package]] name = "pytype" -version = "2024.9.13" +version = "2024.10.11" description = "Python type inferencer" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "pytype-2024.9.13-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:52c0005d220b27f9c933e4077de700c4e8171abce0c2af72f4c6263a85ff5bce"}, - {file = "pytype-2024.9.13-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d5dc847c2fe98bac044f956e2fc9f074f09704b64436522b81ede7dd5fa3653"}, - {file = "pytype-2024.9.13-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529f19141c6170d96a38909df430ca52e6904eaef851ad2690cf632f17d2c195"}, - {file = "pytype-2024.9.13-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:38f3eddf05d8530ef16d3d7c2da2556148b9975fc7c3405ac3073022e1a7434b"}, - {file = "pytype-2024.9.13-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b530eae5ab421a2dc9c4ef53f68629c5a622545150ae9702dbb811f56852a63"}, - {file = "pytype-2024.9.13-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb9eaaaf6c33e2716fdce1cf4166d3e5099372d8898b69ab7673225928096456"}, - {file = "pytype-2024.9.13-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:53b767d85f374c7483c8b2849dceb811a15fcb01520e245dd82bd7c0e2befefb"}, - {file = "pytype-2024.9.13-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:176a5bbc0cb0882918a0b48818b95df2c15811e3a8391da089ffc5b33fea7013"}, - {file = "pytype-2024.9.13-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bdaf1eaaf17a13741f67686c2d4c94c30279cd682c7e4cf535e41fc911b0e59"}, - {file = "pytype-2024.9.13-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:425011cc45fba8c83af796155049f9db89d11e8aedbfb21bc1c99408f4a2c4e3"}, - {file = "pytype-2024.9.13-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e500727967b843488c1978114778162ef00fee9be49dfa5b4758dcbbcc55dd9"}, - {file = "pytype-2024.9.13-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b40beab6ef04fc260d86a8ef47b25d1b525dbc4cfbcb73151fd74210c176df"}, - {file = "pytype-2024.9.13-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:b5fdc24b60938ee846dfbdf08b5ea96e934e7d69c34eb1f8fb7707083d177f0e"}, - {file = "pytype-2024.9.13-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8dcfd509118c2d7e0787e72832b45e30037af1c29dfcb733a7e8014f58337287"}, - {file = "pytype-2024.9.13-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9df731062dc18518a46135c4825ad966e1a275ffc0723dd62f9771b420889da0"}, - {file = "pytype-2024.9.13.tar.gz", hash = "sha256:941046ca0f1c43b79162bb51836fef0ba6608012d99f6833148c249f22216f26"}, + {file = "pytype-2024.10.11-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:1c5a43b132b19928a38ba1dbcf8f4e3f67a41ea26087ccf26ae371c4076c3809"}, + {file = "pytype-2024.10.11-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dd9ecb48aa46ecef14b39f1bbe8ff7e586e499639a056c05bd4436ca0b35d82"}, + {file = "pytype-2024.10.11-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37d8dfdf23679abfdfe047efef7239a438a038e580d7e0767c0403a6be07cea0"}, + {file = "pytype-2024.10.11-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:2e31a964aa82e1ac317adbe17b77010e4f362882df1ce7ad15ef0cf0bb97039f"}, + {file = "pytype-2024.10.11-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15e2f39590cc08ef8e6704cfa5c1db6fbbee2799891f9d8adbf821f883a54745"}, + {file = "pytype-2024.10.11-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ead3408fc9622ba8a357c9a6b9b49059a9b8add0a3b8390a9ab490f62a984005"}, + {file = "pytype-2024.10.11-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:cdc881cce9541a475ec48989a5ab889e6274a85afbf6da0e30266d0823f66d42"}, + {file = "pytype-2024.10.11-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13327d0d17b981fe2660dd3a69f97bf09a526f93debc40bb44b240628e0b55c1"}, + {file = "pytype-2024.10.11-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb98711679e631b01b09b09185504fbf38d60f119280918e244a602cf843b0fe"}, + {file = "pytype-2024.10.11.tar.gz", hash = "sha256:ae5ff82f0b07d5ad68d4ec32a3e8de44fad6ed565a821a76aca50a14df382274"}, ] [package.dependencies] @@ -3228,7 +4111,7 @@ importlab = ">=0.8" jinja2 = ">=3.1.2" libcst = ">=1.0.1" msgspec = ">=0.18.6" -networkx = "<3.2" +networkx = ">=2.8" ninja = ">=1.10.0.post2" pycnite = ">=2024.07.31" pydot = ">=1.4.2" @@ -3249,44 +4132,44 @@ files = [ [[package]] name = "pywin32" -version = "307" +version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" files = [ - {file = "pywin32-307-cp310-cp310-win32.whl", hash = "sha256:f8f25d893c1e1ce2d685ef6d0a481e87c6f510d0f3f117932781f412e0eba31b"}, - {file = "pywin32-307-cp310-cp310-win_amd64.whl", hash = "sha256:36e650c5e5e6b29b5d317385b02d20803ddbac5d1031e1f88d20d76676dd103d"}, - {file = "pywin32-307-cp310-cp310-win_arm64.whl", hash = "sha256:0c12d61e0274e0c62acee79e3e503c312426ddd0e8d4899c626cddc1cafe0ff4"}, - {file = "pywin32-307-cp311-cp311-win32.whl", hash = "sha256:fec5d27cc893178fab299de911b8e4d12c5954e1baf83e8a664311e56a272b75"}, - {file = "pywin32-307-cp311-cp311-win_amd64.whl", hash = "sha256:987a86971753ed7fdd52a7fb5747aba955b2c7fbbc3d8b76ec850358c1cc28c3"}, - {file = "pywin32-307-cp311-cp311-win_arm64.whl", hash = "sha256:fd436897c186a2e693cd0437386ed79f989f4d13d6f353f8787ecbb0ae719398"}, - {file = "pywin32-307-cp312-cp312-win32.whl", hash = "sha256:07649ec6b01712f36debf39fc94f3d696a46579e852f60157a729ac039df0815"}, - {file = "pywin32-307-cp312-cp312-win_amd64.whl", hash = "sha256:00d047992bb5dcf79f8b9b7c81f72e0130f9fe4b22df613f755ab1cc021d8347"}, - {file = "pywin32-307-cp312-cp312-win_arm64.whl", hash = "sha256:b53658acbfc6a8241d72cc09e9d1d666be4e6c99376bc59e26cdb6223c4554d2"}, - {file = "pywin32-307-cp313-cp313-win32.whl", hash = "sha256:ea4d56e48dc1ab2aa0a5e3c0741ad6e926529510516db7a3b6981a1ae74405e5"}, - {file = "pywin32-307-cp313-cp313-win_amd64.whl", hash = "sha256:576d09813eaf4c8168d0bfd66fb7cb3b15a61041cf41598c2db4a4583bf832d2"}, - {file = "pywin32-307-cp313-cp313-win_arm64.whl", hash = "sha256:b30c9bdbffda6a260beb2919f918daced23d32c79109412c2085cbc513338a0a"}, - {file = "pywin32-307-cp37-cp37m-win32.whl", hash = "sha256:5101472f5180c647d4525a0ed289ec723a26231550dbfd369ec19d5faf60e511"}, - {file = "pywin32-307-cp37-cp37m-win_amd64.whl", hash = "sha256:05de55a7c110478dc4b202230e98af5e0720855360d2b31a44bb4e296d795fba"}, - {file = "pywin32-307-cp38-cp38-win32.whl", hash = "sha256:13d059fb7f10792542082f5731d5d3d9645320fc38814759313e5ee97c3fac01"}, - {file = "pywin32-307-cp38-cp38-win_amd64.whl", hash = "sha256:7e0b2f93769d450a98ac7a31a087e07b126b6d571e8b4386a5762eb85325270b"}, - {file = "pywin32-307-cp39-cp39-win32.whl", hash = "sha256:55ee87f2f8c294e72ad9d4261ca423022310a6e79fb314a8ca76ab3f493854c6"}, - {file = "pywin32-307-cp39-cp39-win_amd64.whl", hash = "sha256:e9d5202922e74985b037c9ef46778335c102b74b95cec70f629453dbe7235d87"}, + {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, + {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, + {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, + {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, + {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, + {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, + {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, + {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, + {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] [[package]] name = "pywinpty" -version = "2.0.13" +version = "2.0.14" description = "Pseudo terminal support for Windows from Python." optional = false python-versions = ">=3.8" files = [ - {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, - {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, - {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, - {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, - {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, - {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, + {file = "pywinpty-2.0.14-cp310-none-win_amd64.whl", hash = "sha256:0b149c2918c7974f575ba79f5a4aad58bd859a52fa9eb1296cc22aa412aa411f"}, + {file = "pywinpty-2.0.14-cp311-none-win_amd64.whl", hash = "sha256:cf2a43ac7065b3e0dc8510f8c1f13a75fb8fde805efa3b8cff7599a1ef497bc7"}, + {file = "pywinpty-2.0.14-cp312-none-win_amd64.whl", hash = "sha256:55dad362ef3e9408ade68fd173e4f9032b3ce08f68cfe7eacb2c263ea1179737"}, + {file = "pywinpty-2.0.14-cp313-none-win_amd64.whl", hash = "sha256:074fb988a56ec79ca90ed03a896d40707131897cefb8f76f926e3834227f2819"}, + {file = "pywinpty-2.0.14-cp39-none-win_amd64.whl", hash = "sha256:5725fd56f73c0531ec218663bd8c8ff5acc43c78962fab28564871b5fce053fd"}, + {file = "pywinpty-2.0.14.tar.gz", hash = "sha256:18bd9529e4a5daf2d9719aa17788ba6013e594ae94c5a0c27e83df3278b0660e"}, ] [[package]] @@ -3472,6 +4355,106 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "rapidfuzz" +version = "3.10.1" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.9" +files = [ + {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f17d9f21bf2f2f785d74f7b0d407805468b4c173fa3e52c86ec94436b338e74a"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b31f358a70efc143909fb3d75ac6cd3c139cd41339aa8f2a3a0ead8315731f2b"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4f43f2204b56a61448ec2dd061e26fd344c404da99fb19f3458200c5874ba2"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d81bf186a453a2757472133b24915768abc7c3964194406ed93e170e16c21cb"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3611c8f45379a12063d70075c75134f2a8bd2e4e9b8a7995112ddae95ca1c982"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c3b537b97ac30da4b73930fa8a4fe2f79c6d1c10ad535c5c09726612cd6bed9"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231ef1ec9cf7b59809ce3301006500b9d564ddb324635f4ea8f16b3e2a1780da"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed4f3adc1294834955b7e74edd3c6bd1aad5831c007f2d91ea839e76461a5879"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b6015da2e707bf632a71772a2dbf0703cff6525732c005ad24987fe86e8ec32"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1b35a118d61d6f008e8e3fb3a77674d10806a8972c7b8be433d6598df4d60b01"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc308d79a7e877226f36bdf4e149e3ed398d8277c140be5c1fd892ec41739e6d"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f017dbfecc172e2d0c37cf9e3d519179d71a7f16094b57430dffc496a098aa17"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win32.whl", hash = "sha256:36c0e1483e21f918d0f2f26799fe5ac91c7b0c34220b73007301c4f831a9c4c7"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:10746c1d4c8cd8881c28a87fd7ba0c9c102346dfe7ff1b0d021cdf093e9adbff"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win_arm64.whl", hash = "sha256:dfa64b89dcb906835e275187569e51aa9d546a444489e97aaf2cc84011565fbe"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:92958ae075c87fef393f835ed02d4fe8d5ee2059a0934c6c447ea3417dfbf0e8"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba7521e072c53e33c384e78615d0718e645cab3c366ecd3cc8cb732befd94967"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d02cbd75d283c287471b5b3738b3e05c9096150f93f2d2dfa10b3d700f2db9"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efa1582a397da038e2f2576c9cd49b842f56fde37d84a6b0200ffebc08d82350"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12912acee1f506f974f58de9fdc2e62eea5667377a7e9156de53241c05fdba8"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666d5d8b17becc3f53447bcb2b6b33ce6c2df78792495d1fa82b2924cd48701a"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26f71582c0d62445067ee338ddad99b655a8f4e4ed517a90dcbfbb7d19310474"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a2ef08b27167bcff230ffbfeedd4c4fa6353563d6aaa015d725dd3632fc3de7"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:365e4fc1a2b95082c890f5e98489b894e6bf8c338c6ac89bb6523c2ca6e9f086"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1996feb7a61609fa842e6b5e0c549983222ffdedaf29644cc67e479902846dfe"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:cf654702f144beaa093103841a2ea6910d617d0bb3fccb1d1fd63c54dde2cd49"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec108bf25de674781d0a9a935030ba090c78d49def3d60f8724f3fc1e8e75024"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win32.whl", hash = "sha256:031f8b367e5d92f7a1e27f7322012f3c321c3110137b43cc3bf678505583ef48"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f98f36c6a1bb9a6c8bbec99ad87c8c0e364f34761739b5ea9adf7b48129ae8cf"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:f1da2028cb4e41be55ee797a82d6c1cf589442504244249dfeb32efc608edee7"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1340b56340896bede246f612b6ecf685f661a56aabef3d2512481bfe23ac5835"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2316515169b7b5a453f0ce3adbc46c42aa332cae9f2edb668e24d1fc92b2f2bb"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e06fe6a12241ec1b72c0566c6b28cda714d61965d86569595ad24793d1ab259"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d99c1cd9443b19164ec185a7d752f4b4db19c066c136f028991a480720472e23"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d9aa156ed52d3446388ba4c2f335e312191d1ca9d1f5762ee983cf23e4ecf6"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54bcf4efaaee8e015822be0c2c28214815f4f6b4f70d8362cfecbd58a71188ac"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0c955e32afdbfdf6e9ee663d24afb25210152d98c26d22d399712d29a9b976b"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:191633722203f5b7717efcb73a14f76f3b124877d0608c070b827c5226d0b972"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:195baad28057ec9609e40385991004e470af9ef87401e24ebe72c064431524ab"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0fff4a6b87c07366662b62ae994ffbeadc472e72f725923f94b72a3db49f4671"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4ffed25f9fdc0b287f30a98467493d1e1ce5b583f6317f70ec0263b3c97dbba6"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d02cf8e5af89a9ac8f53c438ddff6d773f62c25c6619b29db96f4aae248177c0"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win32.whl", hash = "sha256:f3bb81d4fe6a5d20650f8c0afcc8f6e1941f6fecdb434f11b874c42467baded0"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:aaf83e9170cb1338922ae42d320699dccbbdca8ffed07faeb0b9257822c26e24"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:c5da802a0d085ad81b0f62828fb55557996c497b2d0b551bbdfeafd6d447892f"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc22d69a1c9cccd560a5c434c0371b2df0f47c309c635a01a913e03bbf183710"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38b0dac2c8e057562b8f0d8ae5b663d2d6a28c5ab624de5b73cef9abb6129a24"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fde3bbb14e92ce8fcb5c2edfff72e474d0080cadda1c97785bf4822f037a309"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9141fb0592e55f98fe9ac0f3ce883199b9c13e262e0bf40c5b18cdf926109d16"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:237bec5dd1bfc9b40bbd786cd27949ef0c0eb5fab5eb491904c6b5df59d39d3c"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18123168cba156ab5794ea6de66db50f21bb3c66ae748d03316e71b27d907b95"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b75fe506c8e02769cc47f5ab21ce3e09b6211d3edaa8f8f27331cb6988779be"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da82aa4b46973aaf9e03bb4c3d6977004648c8638febfc0f9d237e865761270"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c34c022d5ad564f1a5a57a4a89793bd70d7bad428150fb8ff2760b223407cdcf"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e96c84d6c2a0ca94e15acb5399118fff669f4306beb98a6d8ec6f5dccab4412"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e8e154b84a311263e1aca86818c962e1fa9eefdd643d1d5d197fcd2738f88cb9"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:335fee93188f8cd585552bb8057228ce0111bd227fa81bfd40b7df6b75def8ab"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win32.whl", hash = "sha256:6729b856166a9e95c278410f73683957ea6100c8a9d0a8dbe434c49663689255"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e06d99ad1ad97cb2ef7f51ec6b1fedd74a3a700e4949353871cf331d07b382a"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:8d1b7082104d596a3eb012e0549b2634ed15015b569f48879701e9d8db959dbb"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:779027d3307e1a2b1dc0c03c34df87a470a368a1a0840a9d2908baf2d4067956"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:440b5608ab12650d0390128d6858bc839ae77ffe5edf0b33a1551f2fa9860651"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cac41a411e07a6f3dc80dfbd33f6be70ea0abd72e99c59310819d09f07d945"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:958473c9f0bca250590200fd520b75be0dbdbc4a7327dc87a55b6d7dc8d68552"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ef60dfa73749ef91cb6073be1a3e135f4846ec809cc115f3cbfc6fe283a5584"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fbac18f2c19fc983838a60611e67e3262e36859994c26f2ee85bb268de2355"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a0d519ff39db887cd73f4e297922786d548f5c05d6b51f4e6754f452a7f4296"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bebb7bc6aeb91cc57e4881b222484c26759ca865794187217c9dcea6c33adae6"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe07f8b9c3bb5c5ad1d2c66884253e03800f4189a60eb6acd6119ebaf3eb9894"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfa48a4a2d45a41457f0840c48e579db157a927f4e97acf6e20df8fc521c79de"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2cf44d01bfe8ee605b7eaeecbc2b9ca64fc55765f17b304b40ed8995f69d7716"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e6bbca9246d9eedaa1c84e04a7f555493ba324d52ae4d9f3d9ddd1b740dcd87"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win32.whl", hash = "sha256:567f88180f2c1423b4fe3f3ad6e6310fc97b85bdba574801548597287fc07028"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6b2cd7c29d6ecdf0b780deb587198f13213ac01c430ada6913452fd0c40190fc"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win_arm64.whl", hash = "sha256:9f912d459e46607ce276128f52bea21ebc3e9a5ccf4cccfef30dd5bddcf47be8"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac4452f182243cfab30ba4668ef2de101effaedc30f9faabb06a095a8c90fd16"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:565c2bd4f7d23c32834652b27b51dd711814ab614b4e12add8476be4e20d1cf5"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d9747149321607be4ccd6f9f366730078bed806178ec3eeb31d05545e9e8f"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:616290fb9a8fa87e48cb0326d26f98d4e29f17c3b762c2d586f2b35c1fd2034b"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:073a5b107e17ebd264198b78614c0206fa438cce749692af5bc5f8f484883f50"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39c4983e2e2ccb9732f3ac7d81617088822f4a12291d416b09b8a1eadebb3e29"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac7adee6bcf0c6fee495d877edad1540a7e0f5fc208da03ccb64734b43522d7a"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:425f4ac80b22153d391ee3f94bc854668a0c6c129f05cf2eaf5ee74474ddb69e"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65a2fa13e8a219f9b5dcb9e74abe3ced5838a7327e629f426d333dfc8c5a6e66"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75561f3df9a906aaa23787e9992b228b1ab69007932dc42070f747103e177ba8"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edd062490537e97ca125bc6c7f2b7331c2b73d21dc304615afe61ad1691e15d5"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfcc8feccf63245a22dfdd16e222f1a39771a44b870beb748117a0e09cbb4a62"}, + {file = "rapidfuzz-3.10.1.tar.gz", hash = "sha256:5a15546d847a915b3f42dc79ef9b0c78b998b4e2c53b252e7166284066585979"}, +] + +[package.extras] +all = ["numpy"] + [[package]] name = "referencing" version = "0.35.1" @@ -3489,105 +4472,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -3611,6 +4594,20 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + [[package]] name = "rfc3339-validator" version = "0.1.4" @@ -3638,13 +4635,13 @@ files = [ [[package]] name = "rich" -version = "13.9.2" +version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, ] [package.dependencies] @@ -3656,114 +4653,169 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.21.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +files = [ + {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, + {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, + {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, + {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, + {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, + {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, + {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, + {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, + {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, + {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, + {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, +] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.12" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, + {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, ] [[package]] @@ -3822,13 +4874,13 @@ win32 = ["pywin32"] [[package]] name = "sentry-sdk" -version = "2.15.0" +version = "2.19.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.15.0-py2.py3-none-any.whl", hash = "sha256:8fb0d1a4e1a640172f31502e4503543765a1fe8a9209779134a4ac52d4677303"}, - {file = "sentry_sdk-2.15.0.tar.gz", hash = "sha256:a599e7d3400787d6f43327b973e55a087b931ba2c592a7a7afa691f8eb5e75e2"}, + {file = "sentry_sdk-2.19.0-py2.py3-none-any.whl", hash = "sha256:7b0b3b709dee051337244a09a30dbf6e95afe0d34a1f8b430d45e0982a7c125b"}, + {file = "sentry_sdk-2.19.0.tar.gz", hash = "sha256:ee4a4d2ae8bfe3cac012dcf3e4607975904c137e1738116549fc3dbbb6ff0e36"}, ] [package.dependencies] @@ -3852,16 +4904,19 @@ falcon = ["falcon (>=1.4)"] fastapi = ["fastapi (>=0.79.0)"] flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"] +http2 = ["httpcore[http2] (==1.*)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] -huggingface-hub = ["huggingface-hub (>=0.22)"] +huggingface-hub = ["huggingface_hub (>=0.22)"] langchain = ["langchain (>=0.0.210)"] +launchdarkly = ["launchdarkly-server-sdk (>=9.8.0)"] litestar = ["litestar (>=2.0.0)"] loguru = ["loguru (>=0.5)"] openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +openfeature = ["openfeature-sdk (>=0.7.1)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro"] -pure-eval = ["asttokens", "executing", "pure-eval"] +pure-eval = ["asttokens", "executing", "pure_eval"] pymongo = ["pymongo (>=3.1)"] pyspark = ["pyspark (>=2.4.4)"] quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] @@ -3874,23 +4929,23 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "75.1.0" +version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, - {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, + {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, + {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "shellingham" @@ -3914,6 +4969,114 @@ files = [ {file = "simpleeval-0.9.13.tar.gz", hash = "sha256:4a30f9cc01825fe4c719c785e3762623e350c4840d5e6855c2a8496baaa65fac"}, ] +[[package]] +name = "simsimd" +version = "5.9.11" +description = "Portable mixed-precision BLAS-like vector math library for x86 and ARM" +optional = false +python-versions = "*" +files = [ + {file = "simsimd-5.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84a534ccd04d7aa5c4539817e09f94c5c5d4bfee9d72078b89b7e18c811100ac"}, + {file = "simsimd-5.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:323468e396f94eda2494df6b85214f6e4b16812e28cab5eab5ced507aa7221de"}, + {file = "simsimd-5.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f142bbefed325ac74d7209044b2fa777a6737a907fbd39359db6c72271204cfa"}, + {file = "simsimd-5.9.11-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59a89ea757ef18014a56c16096cd80e85ec5f2d71d23068d751747e6154229d4"}, + {file = "simsimd-5.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12d43eaab7bae5ae3e9f0fcbbbe8811eb1e28bb9b7bb68b8a78c8afdcca16f3"}, + {file = "simsimd-5.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca73c0161f47681a2b5e266dfe5fee5b75bc0c0093b978641dd672f38c9c8abf"}, + {file = "simsimd-5.9.11-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:856d59a644e3208512895aa19c52d3fa28f7359ccc6a526c99ec40a0c94d014c"}, + {file = "simsimd-5.9.11-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:79a2a722ccce98375a3ff7033ad21a323b03f41032b004d43817a81baf873b53"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de94d6457949888f17a94ddf165f179ca4f8b83cc9eaedf9a97daeddceae829d"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ecec772486848ccf52e076781591f467c339c6b19dcf66720f8d5b0ede47717d"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a8a211130e8499c60463b77208f51bee04ddb8d3dfece7371bb5e5b878105cdc"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fb7b5c3348a8ba2c4f8dbc16925e83ac4556ff7c98a086008c77d7ee192449b0"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:accaf43fdc9a32c5fb3cc501af91e8a6eb4443f871598f66282e83e705096627"}, + {file = "simsimd-5.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a2b2113f6cee7882f58adab0a7b8939075938addb77df28f5c4f5f88a38a4150"}, + {file = "simsimd-5.9.11-cp310-cp310-win32.whl", hash = "sha256:3b9b112bd2d3f4579b7946463ccaa245cae21ac673c19401b8655ed0984b08dc"}, + {file = "simsimd-5.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:b5030de0fa780e2f33b7b9fc176cea6455205c275bb23fba952c4f25a87fa30e"}, + {file = "simsimd-5.9.11-cp310-cp310-win_arm64.whl", hash = "sha256:a1429f7c48ac6743414e6877554ed18d62e03338162bcc506218869467790ed0"}, + {file = "simsimd-5.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dc3161c6e2f966b06b407ca16a01157e4f62aeb54849102b2381c75afe96de63"}, + {file = "simsimd-5.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a2e1b942270c0e13a242980f6ee28791cbef68842b1365510422e3f3b1108e5"}, + {file = "simsimd-5.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a77dd15b362f71ea95ff9a4eba895d34740261ff56092303e18c7b5584b86eb4"}, + {file = "simsimd-5.9.11-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79f0f9a2aaea47b7feda669592d40c41a3c803d9207ecb96b551e2b10badeb61"}, + {file = "simsimd-5.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3976480e40074dd8ab2e327b0620791f37f88958e23659848d65e9eaee075d69"}, + {file = "simsimd-5.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a925d2ced1d55bb994a77d563cc1cd9be6b628e555d55782ff4844fd2eff40e"}, + {file = "simsimd-5.9.11-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2f08648184772dde6286a532f4034b56be62407d2240f0fa50e9896dd269fd9f"}, + {file = "simsimd-5.9.11-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c9073d17f1ec774c3be6f3ae2bb6022cf329961ead6a53540a852f58a56d80f1"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f318c4aaf8d8fbe168da6bab406a598e8a8710509bcfdb758d4f27ee66991d19"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:332c1abf09ffbc56e8ffa0d4fe91e6505dcc6fe8a4c3212922d7e45047b55210"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f48db0b476dc4f3805cd83050483a3eda59b2c1e4861ca634382c0135d5848c3"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31f5e8b8210ac600910fa0631f094c54564e363ee72881194578ba2630721fce"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:32f0980848ca322fa81f8e9b73291ab780c24fdb23ad976668967830c99cfe09"}, + {file = "simsimd-5.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866adcbfb93840e5b1915e834afda3b244fda8895aa3bdc96bbd0d51f24898f7"}, + {file = "simsimd-5.9.11-cp311-cp311-win32.whl", hash = "sha256:4b4f77da77016b8f7c2ccc8c2203d7f59112b471dc3ee047fdce72fb63f63647"}, + {file = "simsimd-5.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:706e5db8f9b5d3fea9cbf549323c57ef8529d4536cf66784ab7926fb31c3f3d3"}, + {file = "simsimd-5.9.11-cp311-cp311-win_arm64.whl", hash = "sha256:605af1cf0d903f31dc488a94e2e6734d3047baa41d40b362fb3285144b383f63"}, + {file = "simsimd-5.9.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b614a22531f35f9dc752c09da96cc3457f15c5d0ca3e2a12d13d54d2441a476d"}, + {file = "simsimd-5.9.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:95f984148040fd6ffec3bdd8ad68a1750c5bda16c226ff14ccdfc1439705a3b4"}, + {file = "simsimd-5.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46afcd0b7b59fefffdfb91b0e83e881e56b536acb072343cf73d49fbad83bb8d"}, + {file = "simsimd-5.9.11-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6286d20cf837d26a3943504eecb4db5b68046c06797ac125fbad6b5134ee3e"}, + {file = "simsimd-5.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7be158270caeb2e3daf616e052690a5bea41c81b9007d46d0746aee605001616"}, + {file = "simsimd-5.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d2e9f0e7d2b790ceaab1e6860de1026549a20995d93c55d81c590af4df8e82"}, + {file = "simsimd-5.9.11-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d55e497ac4f30c31cb3046f81d18855e007d12ff1673437bac1e1a8c017f67d6"}, + {file = "simsimd-5.9.11-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:42c575afe5f9a8195ff86c4fc019972a373c1a3dd08b2263a3e4fc9f3dd9f3a0"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c3467413ba3343d683f1f40ed48f424ecb1f4f21dcb4d4aa0fab93790a75f375"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a65aad00bbae4a7c28383a925e61f5d43edfeed8afc494e1533e5670b6d74900"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:344d4e276d40eeaf6c724ce3aa309204c49bbc4d64c45e961861053d46557e3f"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d4b7adf20cee0850937550faa1031fc6de5ab2a60d75242608e72809f308c98c"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:36bac4397b6d50dbc63be3fab6bb2d93256c892384b0bbb0ca7eeb9fc1386a60"}, + {file = "simsimd-5.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32f52284c56ed1631054b679151663febeca4a0d265fb11b2d09450e51a80108"}, + {file = "simsimd-5.9.11-cp312-cp312-win32.whl", hash = "sha256:be5cf7833bebdb520fd2a81875ba8740921baba9e0d4ba123041f6b8c358c407"}, + {file = "simsimd-5.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:845172ff6358b285c77311964170e7b50b4de953f8d9f760c8c641cac964966a"}, + {file = "simsimd-5.9.11-cp312-cp312-win_arm64.whl", hash = "sha256:e36a24f31553f86550f6fb3da622c083565d4de7c400bfa80032dd556ae0c2a3"}, + {file = "simsimd-5.9.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:db2134d102f5495a7af97e5544c243b8ea9d25ab1c9f4b5ad9145b9fb07f95c9"}, + {file = "simsimd-5.9.11-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e4803b336f787c45be7da6f28a39ce923b6a868271ea4037e7bd4bc8835478"}, + {file = "simsimd-5.9.11-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8478b76b301da67cbdeb59b839f913461aa3321a1e56ea12c8cfa43277054d6"}, + {file = "simsimd-5.9.11-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e46bd11836155f262797fb6e570e958b251ee7a9c6bc708150d1f4e7cd89721"}, + {file = "simsimd-5.9.11-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:2e8dc07459cf45447c2f23ba793125410af9925fdc5ef5ef2aff6f373bb60358"}, + {file = "simsimd-5.9.11-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:f69c0bf41e8b7782f7dbf1902a35f1c48a62c9bcb957755ad70ecc6a5ffac6a3"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:2d1e8610fe233a480cea6a5acf8b67d291cfe854cf5ead867b62e5569b57d849"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_armv7l.whl", hash = "sha256:574e6475b8632a1e19cff9f8bcf18ae0d7506f22b1a7640bd5ca0c4c86aa69d3"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:7624ebc619325aa9167476b2889fbee9edbbaf93d77608c1b79868029d82f222"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2c6fef446ed48d3d0d9a8f2d296f477c5f667bff38bcaa78247c4c7c5b3ce605"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:d120fbb350ec7287c399583dec6c0483ed897bcc099f877b708588ecdbfa75e9"}, + {file = "simsimd-5.9.11-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:36317c91ae2703ba5415c76bf7a55f6d54a79dbc722f167789f652d5a6b0322e"}, + {file = "simsimd-5.9.11-cp37-cp37m-win32.whl", hash = "sha256:73c67472f8a052522e15fe4c1fe35cd7f37686193452a2cb5d5303780f21a340"}, + {file = "simsimd-5.9.11-cp37-cp37m-win_amd64.whl", hash = "sha256:2aee5a1a1b6528088fa18eeda9357de0b21f635c341f05af4ad684dfb601d2e3"}, + {file = "simsimd-5.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d45725cc3797fd02be2bf8770dcfbd0c2eadef114c3960fb6924a765309549e0"}, + {file = "simsimd-5.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32b58753ff7956649253da75fc68382ddea99b19bef9df56d4b1726ff0a8d94"}, + {file = "simsimd-5.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa9fc6c397ba9f31320d8b9b30068b0bb2857c09a6a01cf2e70892ec18b8012b"}, + {file = "simsimd-5.9.11-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:825ad3c69e306ab35bff789acd2db5d6294852487a7ffa6179e14ecbed4c5316"}, + {file = "simsimd-5.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dd1a635f6e6b682ac594c02eb683f14b2052fbcc0d4ccdf4307c24b1130252a"}, + {file = "simsimd-5.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2297b60d61af009118ff769bda4d778ee5dfb7b557f177396297a5cda998ee1b"}, + {file = "simsimd-5.9.11-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:db2c103ca7a07f2021157e621db113bf5a5f5a6d32b11702aedca4b4054ae18c"}, + {file = "simsimd-5.9.11-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:b9ec8271d3fa7f9b70ed39d3709a721fd5d94c2aa35767f06f7d908c7a55001e"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:230f0df6a887313dad4626e657c7e44e5bc7279eddbdaf74e2e94c5862ccdd43"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:aee92d573d54b9c985000cfbdcabda57cb0fe42ae678dd21f5475e1abd5b6739"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e42e725b040b97f318f2bba489c583ef4ff872987018461ebc2284c8b32ea96a"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:587638a18d9ed36df03a3c728a7fe10b7e79785fc3ce866a35fd58dce9e1f22f"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:70788a80e399afcc787da4ff502f62e04339805b1f2e364f31d6529ee2de03da"}, + {file = "simsimd-5.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a4770ac29c2c02e5d02fbd7125bc7365f008d08f06933559a4c4286e20531a2"}, + {file = "simsimd-5.9.11-cp38-cp38-win32.whl", hash = "sha256:ab572de6a37435c475daa6e5deacc829cb79e028dd7269f463bf51c420e34bc0"}, + {file = "simsimd-5.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:0f976b8e3341ee3099ff247a2bed8e82beec7e74ef634b99b51945e33fab28b7"}, + {file = "simsimd-5.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8e4fef000c8bd3603f5e6884dba5aaf2909ca170be99f41516ef304fcbc9411e"}, + {file = "simsimd-5.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b2bf459923688974ab090e5b67b595aa2d9074c6e3d5cc2e70ca57e2c325b01"}, + {file = "simsimd-5.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5b0a270566ec15d43ce43b1f2b913db3ddd16d230772c29ff2f0402ecffc3d7"}, + {file = "simsimd-5.9.11-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4fba6dfba372229683b7f78b7ff6892601c2eacd861e66e4d84bfa638bd75ed"}, + {file = "simsimd-5.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:362ba4aa418460e8f1e3a2cd13b8dd274525dffc0b26c5a4e75cacf14e8af45b"}, + {file = "simsimd-5.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6cb96639886e69cb1772579536d21204461b775f2383250f5ce5c1e575ad300"}, + {file = "simsimd-5.9.11-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:71ca186e4209e14b2c9ed856e7d831cacf53d6855993eef3417adb030604011b"}, + {file = "simsimd-5.9.11-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:75fca4eb8a0a8ba9058039c0ff30e77ad4d7d5d997340676a0c2c7c62e6d3bd7"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f84adb867f09bea8cc30ca415b2d5716783645e9fb1607ac65492ed8e8efec22"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d64e680c8bd3430f0d74f8f20e0e8e98c5c7631e0d31a3f5cb9700149d647300"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:841447f583b11045bfd4e1427aeeee00678d12f67ddd218cb7614f96898bee5f"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ba227f65df3bed228843f6226d0a55682fc1c58bfb68c6dda4bad394dfbbf535"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b7727c80524768548122eecd5107229e7c1958e97bc666057ce8356703c805a1"}, + {file = "simsimd-5.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3244d8cbc12d2fbc0daf59df7160242871755daabd8cc01e0c905cbdfebbbb1b"}, + {file = "simsimd-5.9.11-cp39-cp39-win32.whl", hash = "sha256:2a1ffe93e781a292f1b1d34b47fbabe82414212e8cb97340428cfe4e800b72c8"}, + {file = "simsimd-5.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:86f24a980c2ac10ad8e6341281c86bc769f84c30f633ba8213d7ee046bbe9599"}, + {file = "simsimd-5.9.11-cp39-cp39-win_arm64.whl", hash = "sha256:0c63ddf5ad90ae2c80309e7763a2d4306738e19f31b614f1cc6d0f784199350a"}, + {file = "simsimd-5.9.11.tar.gz", hash = "sha256:053c034c73aa291cc9189ce90f49ca6c5d4e0b30e4d990a25965c2f516d4a21a"}, +] + [[package]] name = "six" version = "1.16.0" @@ -3925,6 +5088,31 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "smart-open" +version = "7.0.5" +description = "Utils for streaming large files (S3, HDFS, GCS, Azure Blob Storage, gzip, bz2...)" +optional = false +python-versions = "<4.0,>=3.7" +files = [ + {file = "smart_open-7.0.5-py3-none-any.whl", hash = "sha256:8523ed805c12dff3eaa50e9c903a6cb0ae78800626631c5fe7ea073439847b89"}, + {file = "smart_open-7.0.5.tar.gz", hash = "sha256:d3672003b1dbc85e2013e4983b88eb9a5ccfd389b0d4e5015f39a9ee5620ec18"}, +] + +[package.dependencies] +wrapt = "*" + +[package.extras] +all = ["azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "paramiko", "requests", "zstandard"] +azure = ["azure-common", "azure-core", "azure-storage-blob"] +gcs = ["google-cloud-storage (>=2.6.0)"] +http = ["requests"] +s3 = ["boto3"] +ssh = ["paramiko"] +test = ["awscli", "azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "moto[server]", "numpy", "paramiko", "pyopenssl", "pytest", "pytest-benchmark", "pytest-rerunfailures", "requests", "responses", "zstandard"] +webhdfs = ["requests"] +zst = ["zstandard"] + [[package]] name = "sniffio" version = "1.3.1" @@ -3947,6 +5135,152 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] +[[package]] +name = "spacy" +version = "3.8.2" +description = "Industrial-strength Natural Language Processing (NLP) in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "spacy-3.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:795e74493036dda0a576093b797a4fdc1aaa83d66f6c9af0e5b6b1c640dc2222"}, + {file = "spacy-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d432e78fe2a7424aba9a741db07ce58487d3b74fae4e20a779142828e61bc402"}, + {file = "spacy-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536a8ba17359540de502285934bf357805d978128d7bd5e84ba53d28b32a0ffb"}, + {file = "spacy-3.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6a0d0d39baa1cb9f5bb82874cbe1067bf494f76277a383f1f7b29f7a855d41a9"}, + {file = "spacy-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:9dcfcfda558b3e47946b2041c7a4203b78e542d0de20997a7c0a6d11b58b2522"}, + {file = "spacy-3.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5d48918028cff6d69d9915dad64f0e32ebd5f1e4f1fa81a2e17e56a6f61e05"}, + {file = "spacy-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:455f845b88ed795d7e595070ee84b65b3ea382357811e09fc744789a20b7b5f7"}, + {file = "spacy-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05d8a4cbfdb90049053564790a0d12fa790c9471580cb6a1f8bdc2b6e74703dd"}, + {file = "spacy-3.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e3c3e67f786f1410d08420ffcaba0f80dc58387ab6172dcdac1a73353d3a85c7"}, + {file = "spacy-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cfe0c4558f635c67677e36d9a315f51d51f824870589c4846c95e880042a2ceb"}, + {file = "spacy-3.8.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0ce56f3c46dd4cebb5aaa3a40966e813b7fc6a540d547788a7d00cca10cd60a9"}, + {file = "spacy-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09faa873cf23d5136b1c1ce6378054a885f650fda96e1655a3ab49e2e7fdd15b"}, + {file = "spacy-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e992a11de9b727c61288541945c0ffc37ed998aca76bfd557937c2c195d7d4"}, + {file = "spacy-3.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be962a8188fb20d6c2065e1e865d1799ebbf544c1af67ab8c75cb279bf5448c7"}, + {file = "spacy-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:04546c5f5ed607387d4e9ecf57614e90c5784866a10a3c6dbe5b06e9b18a2f29"}, + {file = "spacy-3.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c5fb8b804ebf1c2791b384d61391e9d0227bcfdecd6c861291690813b8a6eb1"}, + {file = "spacy-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3647233b2454e8e7bae94232563c9bff849db9e26bf61ef51122ef723de009fe"}, + {file = "spacy-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca20e2d9b4aeaedd7068d6419762d66cfad82bc8b1e63e36714601686d67f163"}, + {file = "spacy-3.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be3aa3e7d456627acbcb7f585156ee463c01d006a07aeb20b43a8543a02cd047"}, + {file = "spacy-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:54c63d31ef410ebb5b0fd72729afaf50f876bf2bc29f73c6c5fc3676ae4158a1"}, + {file = "spacy-3.8.2.tar.gz", hash = "sha256:4b37ebd25ada4059b0dc9e0893e70dde5df83485329a068ef04580e70892a65d"}, +] + +[package.dependencies] +catalogue = ">=2.0.6,<2.1.0" +cymem = ">=2.0.2,<2.1.0" +jinja2 = "*" +langcodes = ">=3.2.0,<4.0.0" +murmurhash = ">=0.28.0,<1.1.0" +numpy = {version = ">=1.19.0", markers = "python_version >= \"3.9\""} +packaging = ">=20.0" +preshed = ">=3.0.2,<3.1.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" +requests = ">=2.13.0,<3.0.0" +setuptools = "*" +spacy-legacy = ">=3.0.11,<3.1.0" +spacy-loggers = ">=1.0.0,<2.0.0" +srsly = ">=2.4.3,<3.0.0" +thinc = ">=8.3.0,<8.4.0" +tqdm = ">=4.38.0,<5.0.0" +typer = ">=0.3.0,<1.0.0" +wasabi = ">=0.9.1,<1.2.0" +weasel = ">=0.1.0,<0.5.0" + +[package.extras] +apple = ["thinc-apple-ops (>=1.0.0,<2.0.0)"] +cuda = ["cupy (>=5.0.0b4,<13.0.0)"] +cuda-autodetect = ["cupy-wheel (>=11.0.0,<13.0.0)"] +cuda100 = ["cupy-cuda100 (>=5.0.0b4,<13.0.0)"] +cuda101 = ["cupy-cuda101 (>=5.0.0b4,<13.0.0)"] +cuda102 = ["cupy-cuda102 (>=5.0.0b4,<13.0.0)"] +cuda110 = ["cupy-cuda110 (>=5.0.0b4,<13.0.0)"] +cuda111 = ["cupy-cuda111 (>=5.0.0b4,<13.0.0)"] +cuda112 = ["cupy-cuda112 (>=5.0.0b4,<13.0.0)"] +cuda113 = ["cupy-cuda113 (>=5.0.0b4,<13.0.0)"] +cuda114 = ["cupy-cuda114 (>=5.0.0b4,<13.0.0)"] +cuda115 = ["cupy-cuda115 (>=5.0.0b4,<13.0.0)"] +cuda116 = ["cupy-cuda116 (>=5.0.0b4,<13.0.0)"] +cuda117 = ["cupy-cuda117 (>=5.0.0b4,<13.0.0)"] +cuda11x = ["cupy-cuda11x (>=11.0.0,<13.0.0)"] +cuda12x = ["cupy-cuda12x (>=11.5.0,<13.0.0)"] +cuda80 = ["cupy-cuda80 (>=5.0.0b4,<13.0.0)"] +cuda90 = ["cupy-cuda90 (>=5.0.0b4,<13.0.0)"] +cuda91 = ["cupy-cuda91 (>=5.0.0b4,<13.0.0)"] +cuda92 = ["cupy-cuda92 (>=5.0.0b4,<13.0.0)"] +ja = ["sudachidict-core (>=20211220)", "sudachipy (>=0.5.2,!=0.6.1)"] +ko = ["natto-py (>=0.9.0)"] +lookups = ["spacy-lookups-data (>=1.0.3,<1.1.0)"] +th = ["pythainlp (>=2.0)"] +transformers = ["spacy-transformers (>=1.1.2,<1.4.0)"] + +[[package]] +name = "spacy-legacy" +version = "3.0.12" +description = "Legacy registered functions for spaCy backwards compatibility" +optional = false +python-versions = ">=3.6" +files = [ + {file = "spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774"}, + {file = "spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f"}, +] + +[[package]] +name = "spacy-loggers" +version = "1.0.5" +description = "Logging utilities for SpaCy" +optional = false +python-versions = ">=3.6" +files = [ + {file = "spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24"}, + {file = "spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645"}, +] + +[[package]] +name = "srsly" +version = "2.4.8" +description = "Modern high-performance serialization utilities for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "srsly-2.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:17f3bcb418bb4cf443ed3d4dcb210e491bd9c1b7b0185e6ab10b6af3271e63b2"}, + {file = "srsly-2.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b070a58e21ab0e878fd949f932385abb4c53dd0acb6d3a7ee75d95d447bc609"}, + {file = "srsly-2.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98286d20014ed2067ad02b0be1e17c7e522255b188346e79ff266af51a54eb33"}, + {file = "srsly-2.4.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18685084e2e0cc47c25158cbbf3e44690e494ef77d6418c2aae0598c893f35b0"}, + {file = "srsly-2.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:980a179cbf4eb5bc56f7507e53f76720d031bcf0cef52cd53c815720eb2fc30c"}, + {file = "srsly-2.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5472ed9f581e10c32e79424c996cf54c46c42237759f4224806a0cd4bb770993"}, + {file = "srsly-2.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50f10afe9230072c5aad9f6636115ea99b32c102f4c61e8236d8642c73ec7a13"}, + {file = "srsly-2.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c994a89ba247a4d4f63ef9fdefb93aa3e1f98740e4800d5351ebd56992ac75e3"}, + {file = "srsly-2.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7ed4a0c20fa54d90032be32f9c656b6d75445168da78d14fe9080a0c208ad"}, + {file = "srsly-2.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:7a919236a090fb93081fbd1cec030f675910f3863825b34a9afbcae71f643127"}, + {file = "srsly-2.4.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7583c03d114b4478b7a357a1915305163e9eac2dfe080da900555c975cca2a11"}, + {file = "srsly-2.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:94ccdd2f6db824c31266aaf93e0f31c1c43b8bc531cd2b3a1d924e3c26a4f294"}, + {file = "srsly-2.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db72d2974f91aee652d606c7def98744ca6b899bd7dd3009fd75ebe0b5a51034"}, + {file = "srsly-2.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a60c905fd2c15e848ce1fc315fd34d8a9cc72c1dee022a0d8f4c62991131307"}, + {file = "srsly-2.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:e0b8d5722057000694edf105b8f492e7eb2f3aa6247a5f0c9170d1e0d074151c"}, + {file = "srsly-2.4.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:196b4261f9d6372d1d3d16d1216b90c7e370b4141471322777b7b3c39afd1210"}, + {file = "srsly-2.4.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4750017e6d78590b02b12653e97edd25aefa4734281386cc27501d59b7481e4e"}, + {file = "srsly-2.4.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa034cd582ba9e4a120c8f19efa263fcad0f10fc481e73fb8c0d603085f941c4"}, + {file = "srsly-2.4.8-cp36-cp36m-win_amd64.whl", hash = "sha256:5a78ab9e9d177ee8731e950feb48c57380036d462b49e3fb61a67ce529ff5f60"}, + {file = "srsly-2.4.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:087e36439af517e259843df93eb34bb9e2d2881c34fa0f541589bcfbc757be97"}, + {file = "srsly-2.4.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad141d8a130cb085a0ed3a6638b643e2b591cb98a4591996780597a632acfe20"}, + {file = "srsly-2.4.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24d05367b2571c0d08d00459636b951e3ca2a1e9216318c157331f09c33489d3"}, + {file = "srsly-2.4.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3fd661a1c4848deea2849b78f432a70c75d10968e902ca83c07c89c9b7050ab8"}, + {file = "srsly-2.4.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec37233fe39af97b00bf20dc2ceda04d39b9ea19ce0ee605e16ece9785e11f65"}, + {file = "srsly-2.4.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2fd4bc081f1d6a6063396b6d97b00d98e86d9d3a3ac2949dba574a84e148080"}, + {file = "srsly-2.4.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7347cff1eb4ef3fc335d9d4acc89588051b2df43799e5d944696ef43da79c873"}, + {file = "srsly-2.4.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9dc1da5cc94d77056b91ba38365c72ae08556b6345bef06257c7e9eccabafe"}, + {file = "srsly-2.4.8-cp38-cp38-win_amd64.whl", hash = "sha256:dc0bf7b6f23c9ecb49ec0924dc645620276b41e160e9b283ed44ca004c060d79"}, + {file = "srsly-2.4.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff8df21d00d73c371bead542cefef365ee87ca3a5660de292444021ff84e3b8c"}, + {file = "srsly-2.4.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ac3e340e65a9fe265105705586aa56054dc3902789fcb9a8f860a218d6c0a00"}, + {file = "srsly-2.4.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06d1733f4275eff4448e96521cc7dcd8fdabd68ba9b54ca012dcfa2690db2644"}, + {file = "srsly-2.4.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be5b751ad88fdb58fb73871d456248c88204f213aaa3c9aab49b6a1802b3fa8d"}, + {file = "srsly-2.4.8-cp39-cp39-win_amd64.whl", hash = "sha256:822a38b8cf112348f3accbc73274a94b7bf82515cb14a85ba586d126a5a72851"}, + {file = "srsly-2.4.8.tar.gz", hash = "sha256:b24d95a65009c2447e0b49cda043ac53fecf4f09e358d87a57446458f91b8a91"}, +] + +[package.dependencies] +catalogue = ">=2.0.3,<2.1.0" + [[package]] name = "sse-starlette" version = "2.1.3" @@ -3987,13 +5321,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "starlette" -version = "0.38.6" +version = "0.41.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"}, - {file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"}, + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, ] [package.dependencies] @@ -4018,17 +5352,17 @@ widechars = ["wcwidth"] [[package]] name = "temporalio" -version = "1.7.1" +version = "1.8.0" description = "Temporal.io Python SDK" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "temporalio-1.7.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3aff503662255c3e2fa1e9c07b8a702bf8ff1f7d99370b4a6785699c45e75527"}, - {file = "temporalio-1.7.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:0685c7ece5ba639a1af7eb39939726c70c99ffe6c212be3f01f213753923fd88"}, - {file = "temporalio-1.7.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:396e608c227a4f20cde7dfd3c7c8e2d8a9039cd76a40171668bb2b9a84d36bea"}, - {file = "temporalio-1.7.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a38652e2556c384261f81f820b5f65f3b099767f18b9332628ec730cd31f0972"}, - {file = "temporalio-1.7.1-cp38-abi3-win_amd64.whl", hash = "sha256:9b3aea332f6686a424467b026cbc8a1e93c550dfb6f7d983d392090de3d92847"}, - {file = "temporalio-1.7.1.tar.gz", hash = "sha256:8250c391b33dd498dfd7d65a880c72b17e9b0e7cc04e7d444400be74c0fe4c85"}, + {file = "temporalio-1.8.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c6acb217d4bd7297389db756dd9da73ef2bae17f6afee1faa8bf77be200e8b93"}, + {file = "temporalio-1.8.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6ec61660631b2513ce710b468068135280996af105571126295c9645bf29ee22"}, + {file = "temporalio-1.8.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ee13d155dc917e7792b87d1e37b1a0e837c361deb722ccc294edaa5344f2fa2"}, + {file = "temporalio-1.8.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6b67c115b6eaceddae373dc2c597e5ad5dd567282f4bb0ee63c99124f5f0c12d"}, + {file = "temporalio-1.8.0-cp38-abi3-win_amd64.whl", hash = "sha256:6a45571c09859b6cbf33be26dd5d5fab7e7ee3625750a7b91ebde5770e61015b"}, + {file = "temporalio-1.8.0.tar.gz", hash = "sha256:b9e239b8bfd60126a4b591c6e2e691392b69afc8cac9db452d692654bf85f9cc"}, ] [package.dependencies] @@ -4090,6 +5424,90 @@ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] +[[package]] +name = "thefuzz" +version = "0.22.1" +description = "Fuzzy string matching in python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "thefuzz-0.22.1-py3-none-any.whl", hash = "sha256:59729b33556850b90e1093c4cf9e618af6f2e4c985df193fdf3c5b5cf02ca481"}, + {file = "thefuzz-0.22.1.tar.gz", hash = "sha256:7138039a7ecf540da323792d8592ef9902b1d79eb78c147d4f20664de79f3680"}, +] + +[package.dependencies] +rapidfuzz = ">=3.0.0,<4.0.0" + +[[package]] +name = "thinc" +version = "8.3.2" +description = "A refreshing functional take on deep learning, compatible with your favorite libraries" +optional = false +python-versions = ">=3.6" +files = [ + {file = "thinc-8.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6af5b1b57fb1874079f7e84cd99c983c3dcb234a55845d8585d7e066b09755fb"}, + {file = "thinc-8.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f8b753b63714d38f36e951241466c650afe3177b0c8b220e180ebf4888f09f5e"}, + {file = "thinc-8.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d471e1261e5e650f93cfae9880928c2ee68ad0426656f02da4490dd24716a93b"}, + {file = "thinc-8.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b46786063787a60a0d732a5d43d0196f632d3d35780c8fe1232d1378b1b5980"}, + {file = "thinc-8.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:f96c274c4119c92fb8fd8a708381080d47ad92994ef3041c791ed6d4b5c27761"}, + {file = "thinc-8.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:12e998780f40d36d4d5f3b760ef60ac60637643f2965ebe1948801ba44261a03"}, + {file = "thinc-8.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54a5411daaca1718a73982767b714c1d0a5e142de73c916367baf1c13d79e8f0"}, + {file = "thinc-8.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed88275031dcaadd85d3deeb8eb12d1ec0ee6b4679e24cc893c81a30409ac4ee"}, + {file = "thinc-8.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef0868e55108f05300f4508e6896ae4e9492f3415220e3da65579f693225816e"}, + {file = "thinc-8.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:813942d59881c4e4165ce95fef37ba30ce3366dac43289697d13a952a8208854"}, + {file = "thinc-8.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bce8ca6a62ab82f4595210ba7f18bbdb6e33561277c59060f2f04bdb93ac4fbc"}, + {file = "thinc-8.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b014a282e9ea6330a678b472d74f479c7a38168cbf570bdc740e50d960dd78a1"}, + {file = "thinc-8.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80384c228ac6bbaf4ab7f7c9ca4a53c6053f2fb37b2b50c4730b9057f07e9fd"}, + {file = "thinc-8.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ae309b0788478984eafeac4e3c33a2de84a6ea251fd1e3528d8018d4b4347247"}, + {file = "thinc-8.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:fe8dac2749db23f8ebf09d7a7f29e1b99d67e7d7b183e106aa2b6c9b570f3015"}, + {file = "thinc-8.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4b1e4149a3bfdeb308cee4b53b07d234e5b35495a7f35241b80acf7cb4a33d3"}, + {file = "thinc-8.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a6b507b1fecd1771fc448aa27dc42d024e5799d10f1ddad6abc6353ae72ef540"}, + {file = "thinc-8.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4edb20939c3a157beb386aee221a5e1bbfb7ffb90d63d22c80047ca0fa4d026d"}, + {file = "thinc-8.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d701de93d6d6bb029d24088d7f8cb8200f486658fd08dd859767b5eda6eba268"}, + {file = "thinc-8.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:2977c4811b7612984ded795dce182419b9f3058a1a55c191f75024ec2f4cb218"}, + {file = "thinc-8.3.2.tar.gz", hash = "sha256:3e8ef69eac89a601e11d47fc9e43d26ffe7ef682dcf667c94ff35ff690549aeb"}, +] + +[package.dependencies] +blis = ">=1.0.0,<1.1.0" +catalogue = ">=2.0.4,<2.1.0" +confection = ">=0.0.1,<1.0.0" +cymem = ">=2.0.2,<2.1.0" +murmurhash = ">=1.0.2,<1.1.0" +numpy = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3.9\""} +packaging = ">=20.0" +preshed = ">=3.0.2,<3.1.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" +setuptools = "*" +srsly = ">=2.4.0,<3.0.0" +wasabi = ">=0.8.1,<1.2.0" + +[package.extras] +apple = ["thinc-apple-ops (>=1.0.0,<2.0.0)"] +cuda = ["cupy (>=5.0.0b4)"] +cuda-autodetect = ["cupy-wheel (>=11.0.0)"] +cuda100 = ["cupy-cuda100 (>=5.0.0b4)"] +cuda101 = ["cupy-cuda101 (>=5.0.0b4)"] +cuda102 = ["cupy-cuda102 (>=5.0.0b4)"] +cuda110 = ["cupy-cuda110 (>=5.0.0b4)"] +cuda111 = ["cupy-cuda111 (>=5.0.0b4)"] +cuda112 = ["cupy-cuda112 (>=5.0.0b4)"] +cuda113 = ["cupy-cuda113 (>=5.0.0b4)"] +cuda114 = ["cupy-cuda114 (>=5.0.0b4)"] +cuda115 = ["cupy-cuda115 (>=5.0.0b4)"] +cuda116 = ["cupy-cuda116 (>=5.0.0b4)"] +cuda117 = ["cupy-cuda117 (>=5.0.0b4)"] +cuda11x = ["cupy-cuda11x (>=11.0.0)"] +cuda12x = ["cupy-cuda12x (>=11.5.0)"] +cuda80 = ["cupy-cuda80 (>=5.0.0b4)"] +cuda90 = ["cupy-cuda90 (>=5.0.0b4)"] +cuda91 = ["cupy-cuda91 (>=5.0.0b4)"] +cuda92 = ["cupy-cuda92 (>=5.0.0b4)"] +datasets = ["ml-datasets (>=0.2.0,<0.3.0)"] +mxnet = ["mxnet (>=1.5.1,<1.6.0)"] +tensorflow = ["tensorflow (>=2.0.0,<2.6.0)"] +torch = ["torch (>=1.6.0)"] + [[package]] name = "tiktoken" version = "0.7.0" @@ -4144,13 +5562,13 @@ blobfile = ["blobfile (>=2)"] [[package]] name = "tinycss2" -version = "1.3.0" +version = "1.4.0" description = "A tiny CSS parser" optional = false python-versions = ">=3.8" files = [ - {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, - {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, + {file = "tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"}, + {file = "tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7"}, ] [package.dependencies] @@ -4162,111 +5580,26 @@ test = ["pytest", "ruff"] [[package]] name = "tokenizers" -version = "0.20.0" +version = "0.21.0" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "tokenizers-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6cff5c5e37c41bc5faa519d6f3df0679e4b37da54ea1f42121719c5e2b4905c0"}, - {file = "tokenizers-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:62a56bf75c27443432456f4ca5ca055befa95e25be8a28141cc495cac8ae4d6d"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc7de6a63f09c4a86909c2597b995aa66e19df852a23aea894929c74369929"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:053c37ecee482cc958fdee53af3c6534286a86f5d35aac476f7c246830e53ae5"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d7074aaabc151a6363fa03db5493fc95b423b2a1874456783989e96d541c7b6"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a11435780f2acd89e8fefe5e81cecf01776f6edb9b3ac95bcb76baee76b30b90"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a81cd2712973b007d84268d45fc3f6f90a79c31dfe7f1925e6732f8d2959987"}, - {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7dfd796ab9d909f76fb93080e1c7c8309f196ecb316eb130718cd5e34231c69"}, - {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8029ad2aa8cb00605c9374566034c1cc1b15130713e0eb5afcef6cface8255c9"}, - {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ca4d54260ebe97d59dfa9a30baa20d0c4dd9137d99a8801700055c561145c24e"}, - {file = "tokenizers-0.20.0-cp310-none-win32.whl", hash = "sha256:95ee16b57cec11b86a7940174ec5197d506439b0f415ab3859f254b1dffe9df0"}, - {file = "tokenizers-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:0a61a11e93eeadbf02aea082ffc75241c4198e0608bbbac4f65a9026851dcf37"}, - {file = "tokenizers-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6636b798b3c4d6c9b1af1a918bd07c867808e5a21c64324e95318a237e6366c3"}, - {file = "tokenizers-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ec603e42eaf499ffd58b9258162add948717cf21372458132f14e13a6bc7172"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cce124264903a8ea6f8f48e1cc7669e5ef638c18bd4ab0a88769d5f92debdf7f"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07bbeba0231cf8de07aa6b9e33e9779ff103d47042eeeb859a8c432e3292fb98"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06c0ca8397b35d38b83a44a9c6929790c1692957d88541df061cb34d82ebbf08"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca6557ac3b83d912dfbb1f70ab56bd4b0594043916688e906ede09f42e192401"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a5ad94c9e80ac6098328bee2e3264dbced4c6faa34429994d473f795ec58ef4"}, - {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5c7f906ee6bec30a9dc20268a8b80f3b9584de1c9f051671cb057dc6ce28f6"}, - {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:31e087e9ee1b8f075b002bfee257e858dc695f955b43903e1bb4aa9f170e37fe"}, - {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3124fb6f3346cb3d8d775375d3b429bf4dcfc24f739822702009d20a4297990"}, - {file = "tokenizers-0.20.0-cp311-none-win32.whl", hash = "sha256:a4bb8b40ba9eefa621fdcabf04a74aa6038ae3be0c614c6458bd91a4697a452f"}, - {file = "tokenizers-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:2b709d371f1fe60a28ef0c5c67815952d455ca7f34dbe7197eaaed3cc54b658e"}, - {file = "tokenizers-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:15c81a17d0d66f4987c6ca16f4bea7ec253b8c7ed1bb00fdc5d038b1bb56e714"}, - {file = "tokenizers-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a531cdf1fb6dc41c984c785a3b299cb0586de0b35683842a3afbb1e5207f910"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06caabeb4587f8404e0cd9d40f458e9cba3e815c8155a38e579a74ff3e2a4301"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8768f964f23f5b9f50546c0369c75ab3262de926983888bbe8b98be05392a79c"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:626403860152c816f97b649fd279bd622c3d417678c93b4b1a8909b6380b69a8"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c1b88fa9e5ff062326f4bf82681da5a96fca7104d921a6bd7b1e6fcf224af26"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7e559436a07dc547f22ce1101f26d8b2fad387e28ec8e7e1e3b11695d681d8"}, - {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48afb75e50449848964e4a67b0da01261dd3aa8df8daecf10db8fd7f5b076eb"}, - {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:baf5d0e1ff44710a95eefc196dd87666ffc609fd447c5e5b68272a7c3d342a1d"}, - {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e5e56df0e8ed23ba60ae3848c3f069a0710c4b197218fe4f89e27eba38510768"}, - {file = "tokenizers-0.20.0-cp312-none-win32.whl", hash = "sha256:ec53e5ecc142a82432f9c6c677dbbe5a2bfee92b8abf409a9ecb0d425ee0ce75"}, - {file = "tokenizers-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:f18661ece72e39c0dfaa174d6223248a15b457dbd4b0fc07809b8e6d3ca1a234"}, - {file = "tokenizers-0.20.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:f7065b1084d8d1a03dc89d9aad69bcbc8415d4bc123c367063eb32958cd85054"}, - {file = "tokenizers-0.20.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e5d4069e4714e3f7ba0a4d3d44f9d84a432cd4e4aa85c3d7dd1f51440f12e4a1"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:799b808529e54b7e1a36350bda2aeb470e8390e484d3e98c10395cee61d4e3c6"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f9baa027cc8a281ad5f7725a93c204d7a46986f88edbe8ef7357f40a23fb9c7"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:010ec7f3f7a96adc4c2a34a3ada41fa14b4b936b5628b4ff7b33791258646c6b"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98d88f06155335b14fd78e32ee28ca5b2eb30fced4614e06eb14ae5f7fba24ed"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e13eb000ef540c2280758d1b9cfa5fe424b0424ae4458f440e6340a4f18b2638"}, - {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fab3cf066ff426f7e6d70435dc28a9ff01b2747be83810e397cba106f39430b0"}, - {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:39fa3761b30a89368f322e5daf4130dce8495b79ad831f370449cdacfb0c0d37"}, - {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c8da0fba4d179ddf2607821575998df3c294aa59aa8df5a6646dc64bc7352bce"}, - {file = "tokenizers-0.20.0-cp37-none-win32.whl", hash = "sha256:fada996d6da8cf213f6e3c91c12297ad4f6cdf7a85c2fadcd05ec32fa6846fcd"}, - {file = "tokenizers-0.20.0-cp37-none-win_amd64.whl", hash = "sha256:7d29aad702279e0760c265fcae832e89349078e3418dd329732d4503259fd6bd"}, - {file = "tokenizers-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:099c68207f3ef0227ecb6f80ab98ea74de559f7b124adc7b17778af0250ee90a"}, - {file = "tokenizers-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:68012d8a8cddb2eab3880870d7e2086cb359c7f7a2b03f5795044f5abff4e850"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9253bdd209c6aee168deca7d0e780581bf303e0058f268f9bb06859379de19b6"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f868600ddbcb0545905ed075eb7218a0756bf6c09dae7528ea2f8436ebd2c93"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9643d9c8c5f99b6aba43fd10034f77cc6c22c31f496d2f0ee183047d948fa0"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c375c6a889aeab44734028bc65cc070acf93ccb0f9368be42b67a98e1063d3f6"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e359f852328e254f070bbd09a19a568421d23388f04aad9f2fb7da7704c7228d"}, - {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d98b01a309d4387f3b1c1dd68a8b8136af50376cf146c1b7e8d8ead217a5be4b"}, - {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:459f7537119554c2899067dec1ac74a00d02beef6558f4ee2e99513bf6d568af"}, - {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:392b87ec89452628c045c9f2a88bc2a827f4c79e7d84bc3b72752b74c2581f70"}, - {file = "tokenizers-0.20.0-cp38-none-win32.whl", hash = "sha256:55a393f893d2ed4dd95a1553c2e42d4d4086878266f437b03590d3f81984c4fe"}, - {file = "tokenizers-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:30ffe33c5c2f2aab8e9a3340d0110dd9f7ace7eec7362e20a697802306bd8068"}, - {file = "tokenizers-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aa2d4a6fed2a7e3f860c7fc9d48764bb30f2649d83915d66150d6340e06742b8"}, - {file = "tokenizers-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5ef0f814084a897e9071fc4a868595f018c5c92889197bdc4bf19018769b148"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc1e1b791e8c3bf4c4f265f180dadaff1c957bf27129e16fdd5e5d43c2d3762c"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b69e55e481459c07885263743a0d3c18d52db19bae8226a19bcca4aaa213fff"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806b4d82e27a2512bc23057b2986bc8b85824914286975b84d8105ff40d03d9"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9859e9ef13adf5a473ccab39d31bff9c550606ae3c784bf772b40f615742a24f"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef703efedf4c20488a8eb17637b55973745b27997ff87bad88ed499b397d1144"}, - {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eec0061bab94b1841ab87d10831fdf1b48ebaed60e6d66d66dbe1d873f92bf5"}, - {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:980f3d0d7e73f845b69087f29a63c11c7eb924c4ad6b358da60f3db4cf24bdb4"}, - {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c157550a2f3851b29d7fdc9dc059fcf81ff0c0fc49a1e5173a89d533ed043fa"}, - {file = "tokenizers-0.20.0-cp39-none-win32.whl", hash = "sha256:8a3d2f4d08608ec4f9895ec25b4b36a97f05812543190a5f2c3cd19e8f041e5a"}, - {file = "tokenizers-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:d90188d12afd0c75e537f9a1d92f9c7375650188ee4f48fdc76f9e38afbd2251"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d68e15f1815357b059ec266062340c343ea7f98f7f330602df81ffa3474b6122"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:23f9ecec637b9bc80da5f703808d29ed5329e56b5aa8d791d1088014f48afadc"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f830b318ee599e3d0665b3e325f85bc75ee2d2ca6285f52e439dc22b64691580"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3dc750def789cb1de1b5a37657919545e1d9ffa667658b3fa9cb7862407a1b8"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e26e6c755ae884c2ea6135cd215bdd0fccafe4ee62405014b8c3cd19954e3ab9"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a1158c7174f427182e08baa2a8ded2940f2b4a3e94969a85cc9cfd16004cbcea"}, - {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6324826287a3fc198898d3dcf758fe4a8479e42d6039f4c59e2cedd3cf92f64e"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d8653149405bb0c16feaf9cfee327fdb6aaef9dc2998349fec686f35e81c4e2"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a2dc1e402a155e97309287ca085c80eb1b7fab8ae91527d3b729181639fa51"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07bef67b20aa6e5f7868c42c7c5eae4d24f856274a464ae62e47a0f2cccec3da"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da06e397182ff53789c506c7833220c192952c57e1581a53f503d8d953e2d67e"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:302f7e11a14814028b7fc88c45a41f1bbe9b5b35fd76d6869558d1d1809baa43"}, - {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:055ec46e807b875589dfbe3d9259f9a6ee43394fb553b03b3d1e9541662dbf25"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e3144b8acebfa6ae062e8f45f7ed52e4b50fb6c62f93afc8871b525ab9fdcab3"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b52aa3fd14b2a07588c00a19f66511cff5cca8f7266ca3edcdd17f3512ad159f"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b8cf52779ffc5d4d63a0170fbeb512372bad0dd014ce92bbb9149756c831124"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:983a45dd11a876124378dae71d6d9761822199b68a4c73f32873d8cdaf326a5b"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6b819c9a19831ebec581e71a7686a54ab45d90faf3842269a10c11d746de0c"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e738cfd80795fcafcef89c5731c84b05638a4ab3f412f97d5ed7765466576eb1"}, - {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c8842c7be2fadb9c9edcee233b1b7fe7ade406c99b0973f07439985c1c1d0683"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e47a82355511c373a4a430c4909dc1e518e00031207b1fec536c49127388886b"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9afbf359004551179a5db19424180c81276682773cff2c5d002f6eaaffe17230"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07eaa8799a92e6af6f472c21a75bf71575de2af3c0284120b7a09297c0de2f3"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0994b2e5fc53a301071806bc4303e4bc3bdc3f490e92a21338146a36746b0872"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6466e0355b603d10e3cc3d282d350b646341b601e50969464a54939f9848d0"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1e86594c2a433cb1ea09cfbe596454448c566e57ee8905bd557e489d93e89986"}, - {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3e14cdef1efa96ecead6ea64a891828432c3ebba128bdc0596e3059fea104ef3"}, - {file = "tokenizers-0.20.0.tar.gz", hash = "sha256:39d7acc43f564c274085cafcd1dae9d36f332456de1a31970296a6b8da4eac8d"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff"}, + {file = "tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a"}, + {file = "tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c"}, + {file = "tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4"}, ] [package.dependencies] @@ -4290,51 +5623,82 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "tornado" -version = "6.4.1" +version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c"}, + {file = "tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482"}, + {file = "tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38"}, + {file = "tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b"}, ] [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -4356,13 +5720,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typer" -version = "0.12.5" +version = "0.14.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, + {file = "typer-0.14.0-py3-none-any.whl", hash = "sha256:f476233a25770ab3e7b2eebf7c68f3bc702031681a008b20167573a4b7018f09"}, + {file = "typer-0.14.0.tar.gz", hash = "sha256:af58f737f8d0c0c37b9f955a6d39000b9ff97813afcbeef56af5e37cf743b45a"}, ] [package.dependencies] @@ -4373,13 +5737,13 @@ typing-extensions = ">=3.7.4.3" [[package]] name = "types-protobuf" -version = "5.28.0.20240924" +version = "5.28.3.20241030" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-5.28.0.20240924.tar.gz", hash = "sha256:d181af8a256e5a91ce8d5adb53496e880efd9144c7d54483e3653332b60296f0"}, - {file = "types_protobuf-5.28.0.20240924-py3-none-any.whl", hash = "sha256:5cecf612ccdefb7dc95f7a51fb502902f20fc2e6681cd500184aaa1b3931d6a7"}, + {file = "types-protobuf-5.28.3.20241030.tar.gz", hash = "sha256:f7e6b45845d75393fb41c0b3ce82c46d775f9771fae2097414a1dbfe5b51a988"}, + {file = "types_protobuf-5.28.3.20241030-py3-none-any.whl", hash = "sha256:f3dae16adf342d4fb5bb3673cabb22549a6252e5dd66fc52d8310b1a39c64ba9"}, ] [[package]] @@ -4464,6 +5828,57 @@ h11 = ">=0.8" [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +[[package]] +name = "uvloop" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "ward" version = "0.68.0b0" @@ -4485,6 +5900,20 @@ pprintpp = ">=0.4.0,<0.5.0" rich = ">=12.2.0" tomli = ">=1.0.0,<3.0.0" +[[package]] +name = "wasabi" +version = "1.1.3" +description = "A lightweight console printing and formatting toolkit" +optional = false +python-versions = ">=3.6" +files = [ + {file = "wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c"}, + {file = "wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878"}, +] + +[package.dependencies] +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\" and python_version >= \"3.7\""} + [[package]] name = "wat-inspector" version = "0.2.1" @@ -4507,21 +5936,39 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "weasel" +version = "0.4.1" +description = "Weasel: A small and easy workflow system" +optional = false +python-versions = ">=3.7" +files = [ + {file = "weasel-0.4.1-py3-none-any.whl", hash = "sha256:24140a090ea1ac512a2b2f479cc64192fd1d527a7f3627671268d08ed5ac418c"}, + {file = "weasel-0.4.1.tar.gz", hash = "sha256:aabc210f072e13f6744e5c3a28037f93702433405cd35673f7c6279147085aa9"}, +] + +[package.dependencies] +cloudpathlib = ">=0.7.0,<1.0.0" +confection = ">=0.0.4,<0.2.0" +packaging = ">=20.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" +requests = ">=2.13.0,<3.0.0" +smart-open = ">=5.2.1,<8.0.0" +srsly = ">=2.4.3,<3.0.0" +typer = ">=0.3.0,<1.0.0" +wasabi = ">=0.9.1,<1.2.0" + [[package]] name = "webcolors" -version = "24.8.0" +version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, - {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, + {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, + {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, ] -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["coverage[toml]"] - [[package]] name = "webencodings" version = "0.5.1" @@ -4560,120 +6007,317 @@ files = [ {file = "widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6"}, ] +[[package]] +name = "wrapt" +version = "1.17.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +files = [ + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +description = "Python binding for xxHash" +optional = false +python-versions = ">=3.7" +files = [ + {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"}, + {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"}, + {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"}, + {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"}, + {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"}, + {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"}, + {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"}, + {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"}, + {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"}, + {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"}, + {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"}, + {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"}, + {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"}, + {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"}, + {file = "xxhash-3.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6e5f70f6dca1d3b09bccb7daf4e087075ff776e3da9ac870f86ca316736bb4aa"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e76e83efc7b443052dd1e585a76201e40b3411fe3da7af4fe434ec51b2f163b"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33eac61d0796ca0591f94548dcfe37bb193671e0c9bcf065789b5792f2eda644"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ec70a89be933ea49222fafc3999987d7899fc676f688dd12252509434636622"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86b8e7f703ec6ff4f351cfdb9f428955859537125904aa8c963604f2e9d3e7"}, + {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0adfbd36003d9f86c8c97110039f7539b379f28656a04097e7434d3eaf9aa131"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:63107013578c8a730419adc05608756c3fa640bdc6abe806c3123a49fb829f43"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:683b94dbd1ca67557850b86423318a2e323511648f9f3f7b1840408a02b9a48c"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5d2a01dcce81789cf4b12d478b5464632204f4c834dc2d064902ee27d2d1f0ee"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a9d360a792cbcce2fe7b66b8d51274ec297c53cbc423401480e53b26161a290d"}, + {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f0b48edbebea1b7421a9c687c304f7b44d0677c46498a046079d445454504737"}, + {file = "xxhash-3.5.0-cp37-cp37m-win32.whl", hash = "sha256:7ccb800c9418e438b44b060a32adeb8393764da7441eb52aa2aa195448935306"}, + {file = "xxhash-3.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c3bc7bf8cb8806f8d1c9bf149c18708cb1c406520097d6b0a73977460ea03602"}, + {file = "xxhash-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74752ecaa544657d88b1d1c94ae68031e364a4d47005a90288f3bab3da3c970f"}, + {file = "xxhash-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee1316133c9b463aa81aca676bc506d3f80d8f65aeb0bba2b78d0b30c51d7bd"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:602d339548d35a8579c6b013339fb34aee2df9b4e105f985443d2860e4d7ffaa"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:695735deeddfb35da1677dbc16a083445360e37ff46d8ac5c6fcd64917ff9ade"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1030a39ba01b0c519b1a82f80e8802630d16ab95dc3f2b2386a0b5c8ed5cbb10"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5bc08f33c4966f4eb6590d6ff3ceae76151ad744576b5fc6c4ba8edd459fdec"}, + {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160e0c19ee500482ddfb5d5570a0415f565d8ae2b3fd69c5dcfce8a58107b1c3"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f1abffa122452481a61c3551ab3c89d72238e279e517705b8b03847b1d93d738"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5e9db7ef3ecbfc0b4733579cea45713a76852b002cf605420b12ef3ef1ec148"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:23241ff6423378a731d84864bf923a41649dc67b144debd1077f02e6249a0d54"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:82b833d5563fefd6fceafb1aed2f3f3ebe19f84760fdd289f8b926731c2e6e91"}, + {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a80ad0ffd78bef9509eee27b4a29e56f5414b87fb01a888353e3d5bda7038bd"}, + {file = "xxhash-3.5.0-cp38-cp38-win32.whl", hash = "sha256:50ac2184ffb1b999e11e27c7e3e70cc1139047e7ebc1aa95ed12f4269abe98d4"}, + {file = "xxhash-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:392f52ebbb932db566973693de48f15ce787cabd15cf6334e855ed22ea0be5b3"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"}, + {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"}, + {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"}, + {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b4154c00eb22e4d543f472cfca430e7962a0f1d0f3778334f2e08a7ba59363c"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d30bbc1644f726b825b3278764240f449d75f1a8bdda892e641d4a688b1494ae"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0b72f2423e2aa53077e54a61c28e181d23effeaafd73fcb9c494e60930c8e"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13de2b76c1835399b2e419a296d5b38dc4855385d9e96916299170085ef72f57"}, + {file = "xxhash-3.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0691bfcc4f9c656bcb96cc5db94b4d75980b9d5589f2e59de790091028580837"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:297595fe6138d4da2c8ce9e72a04d73e58725bb60f3a19048bc96ab2ff31c692"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1276d369452040cbb943300dc8abeedab14245ea44056a2943183822513a18"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2061188a1ba352fc699c82bff722f4baacb4b4b8b2f0c745d2001e56d0dfb514"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c384c434021e4f62b8d9ba0bc9467e14d394893077e2c66d826243025e1f81"}, + {file = "xxhash-3.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e6a4dd644d72ab316b580a1c120b375890e4c52ec392d4aef3c63361ec4d77d1"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"}, + {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"}, +] + [[package]] name = "yarl" -version = "1.13.1" +version = "1.18.0" description = "Yet another URL library" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:82e692fb325013a18a5b73a4fed5a1edaa7c58144dc67ad9ef3d604eccd451ad"}, - {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df4e82e68f43a07735ae70a2d84c0353e58e20add20ec0af611f32cd5ba43fb4"}, - {file = "yarl-1.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec9dd328016d8d25702a24ee274932aebf6be9787ed1c28d021945d264235b3c"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5820bd4178e6a639b3ef1db8b18500a82ceab6d8b89309e121a6859f56585b05"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86c438ce920e089c8c2388c7dcc8ab30dfe13c09b8af3d306bcabb46a053d6f7"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3de86547c820e4f4da4606d1c8ab5765dd633189791f15247706a2eeabc783ae"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca53632007c69ddcdefe1e8cbc3920dd88825e618153795b57e6ebcc92e752a"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4ee1d240b84e2f213565f0ec08caef27a0e657d4c42859809155cf3a29d1735"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c49f3e379177f4477f929097f7ed4b0622a586b0aa40c07ac8c0f8e40659a1ac"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5c5e32fef09ce101fe14acd0f498232b5710effe13abac14cd95de9c274e689e"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab9524e45ee809a083338a749af3b53cc7efec458c3ad084361c1dbf7aaf82a2"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b1481c048fe787f65e34cb06f7d6824376d5d99f1231eae4778bbe5c3831076d"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31497aefd68036d8e31bfbacef915826ca2e741dbb97a8d6c7eac66deda3b606"}, - {file = "yarl-1.13.1-cp310-cp310-win32.whl", hash = "sha256:1fa56f34b2236f5192cb5fceba7bbb09620e5337e0b6dfe2ea0ddbd19dd5b154"}, - {file = "yarl-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:1bbb418f46c7f7355084833051701b2301092e4611d9e392360c3ba2e3e69f88"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:216a6785f296169ed52cd7dcdc2612f82c20f8c9634bf7446327f50398732a51"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40c6e73c03a6befb85b72da213638b8aaa80fe4136ec8691560cf98b11b8ae6e"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2430cf996113abe5aee387d39ee19529327205cda975d2b82c0e7e96e5fdabdc"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fb4134cc6e005b99fa29dbc86f1ea0a298440ab6b07c6b3ee09232a3b48f495"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309c104ecf67626c033845b860d31594a41343766a46fa58c3309c538a1e22b2"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f90575e9fe3aae2c1e686393a9689c724cd00045275407f71771ae5d690ccf38"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2e1626be8712333a9f71270366f4a132f476ffbe83b689dd6dc0d114796c74"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b66c87da3c6da8f8e8b648878903ca54589038a0b1e08dde2c86d9cd92d4ac9"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cf1ad338620249f8dd6d4b6a91a69d1f265387df3697ad5dc996305cf6c26fb2"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9915300fe5a0aa663c01363db37e4ae8e7c15996ebe2c6cce995e7033ff6457f"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:703b0f584fcf157ef87816a3c0ff868e8c9f3c370009a8b23b56255885528f10"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1d8e3ca29f643dd121f264a7c89f329f0fcb2e4461833f02de6e39fef80f89da"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7055bbade838d68af73aea13f8c86588e4bcc00c2235b4b6d6edb0dbd174e246"}, - {file = "yarl-1.13.1-cp311-cp311-win32.whl", hash = "sha256:a3442c31c11088e462d44a644a454d48110f0588de830921fd201060ff19612a"}, - {file = "yarl-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:81bad32c8f8b5897c909bf3468bf601f1b855d12f53b6af0271963ee67fff0d2"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f452cc1436151387d3d50533523291d5f77c6bc7913c116eb985304abdbd9ec9"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9cec42a20eae8bebf81e9ce23fb0d0c729fc54cf00643eb251ce7c0215ad49fe"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d959fe96e5c2712c1876d69af0507d98f0b0e8d81bee14cfb3f6737470205419"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8c837ab90c455f3ea8e68bee143472ee87828bff19ba19776e16ff961425b57"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94a993f976cdcb2dc1b855d8b89b792893220db8862d1a619efa7451817c836b"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b2442a415a5f4c55ced0fade7b72123210d579f7d950e0b5527fc598866e62c"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fdbf0418489525231723cdb6c79e7738b3cbacbaed2b750cb033e4ea208f220"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b7f6e699304717fdc265a7e1922561b02a93ceffdaefdc877acaf9b9f3080b8"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bcd5bf4132e6a8d3eb54b8d56885f3d3a38ecd7ecae8426ecf7d9673b270de43"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a93a4557f7fc74a38ca5a404abb443a242217b91cd0c4840b1ebedaad8919d4"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:22b739f99c7e4787922903f27a892744189482125cc7b95b747f04dd5c83aa9f"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2db874dd1d22d4c2c657807562411ffdfabec38ce4c5ce48b4c654be552759dc"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4feaaa4742517eaceafcbe74595ed335a494c84634d33961214b278126ec1485"}, - {file = "yarl-1.13.1-cp312-cp312-win32.whl", hash = "sha256:bbf9c2a589be7414ac4a534d54e4517d03f1cbb142c0041191b729c2fa23f320"}, - {file = "yarl-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:d07b52c8c450f9366c34aa205754355e933922c79135125541daae6cbf31c799"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:95c6737f28069153c399d875317f226bbdea939fd48a6349a3b03da6829fb550"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cd66152561632ed4b2a9192e7f8e5a1d41e28f58120b4761622e0355f0fe034c"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6a2acde25be0cf9be23a8f6cbd31734536a264723fca860af3ae5e89d771cd71"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18595e6a2ee0826bf7dfdee823b6ab55c9b70e8f80f8b77c37e694288f5de1"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a31d21089894942f7d9a8df166b495101b7258ff11ae0abec58e32daf8088813"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45f209fb4bbfe8630e3d2e2052535ca5b53d4ce2d2026bed4d0637b0416830da"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f722f30366474a99745533cc4015b1781ee54b08de73260b2bbe13316079851"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3bf60444269345d712838bb11cc4eadaf51ff1a364ae39ce87a5ca8ad3bb2c8"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:942c80a832a79c3707cca46bd12ab8aa58fddb34b1626d42b05aa8f0bcefc206"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:44b07e1690f010c3c01d353b5790ec73b2f59b4eae5b0000593199766b3f7a5c"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:396e59b8de7e4d59ff5507fb4322d2329865b909f29a7ed7ca37e63ade7f835c"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3bb83a0f12701c0b91112a11148b5217617982e1e466069d0555be9b372f2734"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c92b89bffc660f1274779cb6fbb290ec1f90d6dfe14492523a0667f10170de26"}, - {file = "yarl-1.13.1-cp313-cp313-win32.whl", hash = "sha256:269c201bbc01d2cbba5b86997a1e0f73ba5e2f471cfa6e226bcaa7fd664b598d"}, - {file = "yarl-1.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:1d0828e17fa701b557c6eaed5edbd9098eb62d8838344486248489ff233998b8"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8be8cdfe20787e6a5fcbd010f8066227e2bb9058331a4eccddec6c0db2bb85b2"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08d7148ff11cb8e886d86dadbfd2e466a76d5dd38c7ea8ebd9b0e07946e76e4b"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4afdf84610ca44dcffe8b6c22c68f309aff96be55f5ea2fa31c0c225d6b83e23"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0d12fe78dcf60efa205e9a63f395b5d343e801cf31e5e1dda0d2c1fb618073d"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298c1eecfd3257aa16c0cb0bdffb54411e3e831351cd69e6b0739be16b1bdaa8"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c14c16831b565707149c742d87a6203eb5597f4329278446d5c0ae7a1a43928e"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9bacedbb99685a75ad033fd4de37129449e69808e50e08034034c0bf063f99"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:658e8449b84b92a4373f99305de042b6bd0d19bf2080c093881e0516557474a5"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:373f16f38721c680316a6a00ae21cc178e3a8ef43c0227f88356a24c5193abd6"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:45d23c4668d4925688e2ea251b53f36a498e9ea860913ce43b52d9605d3d8177"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f7917697bcaa3bc3e83db91aa3a0e448bf5cde43c84b7fc1ae2427d2417c0224"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5989a38ba1281e43e4663931a53fbf356f78a0325251fd6af09dd03b1d676a09"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11b3ca8b42a024513adce810385fcabdd682772411d95bbbda3b9ed1a4257644"}, - {file = "yarl-1.13.1-cp38-cp38-win32.whl", hash = "sha256:dcaef817e13eafa547cdfdc5284fe77970b891f731266545aae08d6cce52161e"}, - {file = "yarl-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:7addd26594e588503bdef03908fc207206adac5bd90b6d4bc3e3cf33a829f57d"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0ae6637b173d0c40b9c1462e12a7a2000a71a3258fa88756a34c7d38926911c"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:576365c9f7469e1f6124d67b001639b77113cfd05e85ce0310f5f318fd02fe85"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78f271722423b2d4851cf1f4fa1a1c4833a128d020062721ba35e1a87154a049"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d74f3c335cfe9c21ea78988e67f18eb9822f5d31f88b41aec3a1ec5ecd32da5"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1891d69a6ba16e89473909665cd355d783a8a31bc84720902c5911dbb6373465"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb382fd7b4377363cc9f13ba7c819c3c78ed97c36a82f16f3f92f108c787cbbf"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8854b9f80693d20cec797d8e48a848c2fb273eb6f2587b57763ccba3f3bd4b"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbf2c3f04ff50f16404ce70f822cdc59760e5e2d7965905f0e700270feb2bbfc"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fb9f59f3848edf186a76446eb8bcf4c900fe147cb756fbbd730ef43b2e67c6a7"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ef9b85fa1bc91c4db24407e7c4da93a5822a73dd4513d67b454ca7064e8dc6a3"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:098b870c18f1341786f290b4d699504e18f1cd050ed179af8123fd8232513424"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8c723c91c94a3bc8033dd2696a0f53e5d5f8496186013167bddc3fb5d9df46a3"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44a4c40a6f84e4d5955b63462a0e2a988f8982fba245cf885ce3be7618f6aa7d"}, - {file = "yarl-1.13.1-cp39-cp39-win32.whl", hash = "sha256:84bbcdcf393139f0abc9f642bf03f00cac31010f3034faa03224a9ef0bb74323"}, - {file = "yarl-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:fc2931ac9ce9c61c9968989ec831d3a5e6fcaaff9474e7cfa8de80b7aff5a093"}, - {file = "yarl-1.13.1-py3-none-any.whl", hash = "sha256:6a5185ad722ab4dd52d5fb1f30dcc73282eb1ed494906a92d1a228d3f89607b0"}, - {file = "yarl-1.13.1.tar.gz", hash = "sha256:ec8cfe2295f3e5e44c51f57272afbd69414ae629ec7c6b27f5a410efc78b70a0"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9"}, + {file = "yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0"}, + {file = "yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc"}, + {file = "yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716"}, + {file = "yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7"}, + {file = "yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75"}, + {file = "yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af"}, + {file = "yarl-1.18.0-cp313-cp313-win32.whl", hash = "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88"}, + {file = "yarl-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89"}, + {file = "yarl-1.18.0-cp39-cp39-win32.whl", hash = "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f"}, + {file = "yarl-1.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5"}, + {file = "yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0"}, + {file = "yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -4687,4 +6331,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.12,<3.13" -content-hash = "04b888dec8224adfc326c796eda70f96df1ab93905cbfe3ebda00330b2740e48" +content-hash = "49d72de9b129ab135480de0f7ddf2dd0c4e120dc69b49bf581ca9f78c386e6f3" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 1701a45cd..024d4fc8f 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -8,7 +8,7 @@ packages = [{include = "agents_api"}] [tool.poetry.dependencies] python = ">=3.12,<3.13" -fastapi = "^0.112.1" +fastapi = "^0.115.4" pycozo = {extras = ["embedded"], version = "^0.7.6"} uvicorn = "^0.30.6" fire = "^0.5.0" @@ -17,14 +17,13 @@ pandas = "^2.2.2" openai = "^1.41.0" httpx = "^0.27.0" sentry-sdk = {extras = ["fastapi"], version = "^2.13.0"} -temporalio = "^1.7.0" +temporalio = ">=1.8" pydantic = {extras = ["email"], version = "^2.9.2"} arrow = "^1.3.0" jinja2 = "^3.1.4" jinja2schema = "^0.1.4" jsonschema = "^4.21.1" -litellm = "^1.46.7" -numpy = "^2.1.0" +numpy = ">=2.0.0,<2.1.0" tiktoken = "^0.7.0" tenacity = "^9.0.0" beartype = "^0.18.5" @@ -32,19 +31,37 @@ pydantic-partial = "^0.5.5" simpleeval = "^0.9.13" lz4 = "^4.3.3" -pyyaml = "^6.0.2" google-re2 = "^1.1.20240702" scalar-fastapi = "^1.0.3" sse-starlette = "^2.1.3" anyio = "^4.4.0" -python-box = {extras = ["toml"], version = "^7.2.0"} +python-box = {extras = [], version = "^7.2.0"} +prometheus-fastapi-instrumentator = "^7.0.0" +prometheus-client = "^0.21.0" +xxhash = "^3.5.0" + +spacy = "^3.8.2" +en-core-web-sm = {url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl"} +msgpack = "^1.1.0" +thefuzz = "^0.22.1" +gunicorn = "^23.0.0" +uvloop = "^0.21.0" +anthropic = "^0.37.1" +simsimd = "^5.9.4" +langchain-core = "^0.3.14" +litellm = {git = "https://github.com/julep-ai/litellm.git", rev = "fix_anthropic_tool_image_content"} +larch-pickle = "^1.4.3" +aiobotocore = "^2.15.2" +async-lru = "^2.0.4" +pycozo-async = "^0.7.6" + [tool.poetry.group.dev.dependencies] ipython = "^8.26.0" ruff = "^0.5.5" -datamodel-code-generator = "^0.25.9" +datamodel-code-generator = "^0.26.2" cozo-migrate = "^0.2.0" poethepoet = "^0.25.1" -pytype = ">=2024.9.13" +pytype = ">=2024.10.11" pyjwt = "^2.8.0" ward = "^0.68.0b0" jupyterlab = "^4.2.4" @@ -52,6 +69,7 @@ ipywidgets = "^8.1.3" wat-inspector = "^0.2.1" julep = ">=1.0,<2.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" @@ -95,12 +113,15 @@ datamodel-codegen \ --use-exact-imports \ --use-standard-collections \ --use-non-positive-negative-number-constrained-types \ - --target-python-version 3.11 \ + --target-python-version 3.12 \ + --treat-dot-as-module \ + --use-title-as-name \ --collapse-root-models \ + --output-datetime-class AwareDatetime \ --openapi-scopes schemas \ --keep-model-order \ --disable-timestamp""" [tool.poe.tasks.test] env = { AGENTS_API_TESTING = "true", PYTHONPATH = "{PYTHONPATH}:." } -cmd = "ward test" +cmd = "ward test --exclude .venv" diff --git a/agents-api/pytype.toml b/agents-api/pytype.toml index b591601cc..e3995a98e 100644 --- a/agents-api/pytype.toml +++ b/agents-api/pytype.toml @@ -8,7 +8,9 @@ exclude = [ # Space-separated list of files or directories to process. inputs = [ - '.', + 'agents_api', + 'migrations', + 'tests', ] # Keep going past errors to analyze as many files as possible. diff --git a/agents-api/scripts/agents_api.py b/agents-api/scripts/agents_api.py index 8ab7d2e0c..5bacef0c8 100644 --- a/agents-api/scripts/agents_api.py +++ b/agents-api/scripts/agents_api.py @@ -1,4 +1,5 @@ import fire + from agents_api.web import main diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 9ae198c78..222b6891c 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,3 +1,4 @@ +import time from uuid import UUID, uuid4 from cozo_migrate.api import apply, init @@ -10,6 +11,7 @@ CreateAgentRequest, CreateDocRequest, CreateExecutionRequest, + CreateFileRequest, CreateSessionRequest, CreateTaskRequest, CreateToolRequest, @@ -27,6 +29,8 @@ create_execution_transition, ) from agents_api.models.execution.create_temporal_lookup import create_temporal_lookup +from agents_api.models.files.create_file import create_file +from agents_api.models.files.delete_file import delete_file from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.task.create_task import create_task @@ -36,7 +40,12 @@ from agents_api.models.user.create_user import create_user from agents_api.models.user.delete_user import delete_user from agents_api.web import app -from tests.utils import patch_embed_acompletion as patch_embed_acompletion_ctx +from tests.utils import ( + patch_embed_acompletion as patch_embed_acompletion_ctx, +) +from tests.utils import ( + patch_s3_client, +) EMBEDDING_SIZE: int = 1024 @@ -80,6 +89,28 @@ def test_developer_id(cozo_client=cozo_client): ) +@fixture(scope="global") +def test_file(client=cozo_client, developer_id=test_developer_id): + file = create_file( + developer_id=developer_id, + data=CreateFileRequest( + name="Hello", + description="World", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ), + client=client, + ) + + yield file + + delete_file( + developer_id=developer_id, + file_id=file.id, + client=client, + ) + + @fixture(scope="global") def test_developer(cozo_client=cozo_client, developer_id=test_developer_id): return get_developer( @@ -148,8 +179,7 @@ def test_session( session = create_session( developer_id=developer_id, data=CreateSessionRequest( - agent=test_agent.id, - user=test_user.id, + agent=test_agent.id, user=test_user.id, metadata={"test": "test"} ), client=cozo_client, ) @@ -177,6 +207,8 @@ def test_doc( client=client, ) + time.sleep(0.5) + yield doc delete_doc( @@ -202,6 +234,8 @@ def test_user_doc( client=client, ) + time.sleep(0.5) + yield doc delete_doc( @@ -410,3 +444,9 @@ def _make_request(method, url, **kwargs): return client.request(method, url, headers=headers, **kwargs) return _make_request + + +@fixture(scope="global") +def s3_client(): + with patch_s3_client() as s3_client: + yield s3_client diff --git a/agents-api/tests/sample_tasks/simple_multi_step.yaml b/agents-api/tests/sample_tasks/simple_multi_step.yaml index 7153ecb9c..3e05527f4 100644 --- a/agents-api/tests/sample_tasks/simple_multi_step.yaml +++ b/agents-api/tests/sample_tasks/simple_multi_step.yaml @@ -20,4 +20,4 @@ main: - log: _ - evaluate: - result: _["choices"][0]["message"].content.strip() \ No newline at end of file + result: '[item["choices"][0]["message"].content.strip() for item in _]' diff --git a/agents-api/tests/test_agent_routes.py b/agents-api/tests/test_agent_routes.py index f053671a0..91ddf9f1a 100644 --- a/agents-api/tests/test_agent_routes.py +++ b/agents-api/tests/test_agent_routes.py @@ -210,3 +210,21 @@ def _(make_request=make_request): assert isinstance(agents, list) assert len(agents) > 0 + + +@test("route: list agents with metadata filter") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/agents", + params={ + "metadata_filter": {"test": "test"}, + }, + ) + + assert response.status_code == 200 + response = response.json() + agents = response["items"] + + assert isinstance(agents, list) + assert len(agents) > 0 diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index b0f886c4f..a7fa7868a 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -1,5 +1,7 @@ # Tests for entry queries +import asyncio + from ward import test from agents_api.autogen.openapi_model import CreateDocRequest @@ -41,7 +43,6 @@ def _( ) -# TODO: Execute embedding workflow to fix this test and other docs tests @test("model: get docs") def _(client=cozo_client, doc=test_doc, developer_id=test_developer_id): get_doc( @@ -79,13 +80,14 @@ def _( owner_type="agent", owner_id=agent.id, client=client, + include_without_embeddings=True, ) assert len(result) >= 1 @test("model: search docs by text") -def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): +async def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): create_doc( developer_id=developer_id, owner_type="agent", @@ -96,6 +98,8 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): client=client, ) + await asyncio.sleep(1) + result = search_docs_by_text( developer_id=developer_id, owners=[("agent", agent.id)], @@ -104,10 +108,11 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): ) assert len(result) >= 1 + assert result[0].metadata is not None @test("model: search docs by embedding") -def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): +async def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): doc = create_doc( developer_id=developer_id, owner_type="agent", @@ -125,6 +130,8 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): client=client, ) + await asyncio.sleep(1) + ### Search query_embedding = [0.99] * EMBEDDING_SIZE @@ -136,6 +143,7 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): ) assert len(result) >= 1 + assert result[0].metadata is not None @test("model: embed snippets") diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index d61bfbcb7..89a14a41c 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -1,4 +1,6 @@ -from ward import test +import time + +from ward import skip, test from tests.fixtures import ( make_request, @@ -132,9 +134,44 @@ def _(make_request=make_request, agent=test_agent): assert isinstance(docs, list) +@test("route: list user docs with metadata filter") +def _(make_request=make_request, user=test_user): + response = make_request( + method="GET", + url=f"/users/{user.id}/docs", + params={ + "metadata_filter": {"test": "test"}, + }, + ) + + assert response.status_code == 200 + response = response.json() + docs = response["items"] + + assert isinstance(docs, list) + + +@test("route: list agent docs with metadata filter") +def _(make_request=make_request, agent=test_agent): + response = make_request( + method="GET", + url=f"/agents/{agent.id}/docs", + params={ + "metadata_filter": {"test": "test"}, + }, + ) + + assert response.status_code == 200 + response = response.json() + docs = response["items"] + + assert isinstance(docs, list) + + # TODO: Fix this test. It fails sometimes and sometimes not. @test("route: search agent docs") async def _(make_request=make_request, agent=test_agent, doc=test_doc): + time.sleep(0.5) search_params = dict( text=doc.content[0], limit=1, @@ -154,8 +191,11 @@ async def _(make_request=make_request, agent=test_agent, doc=test_doc): assert len(docs) >= 1 +# FIXME: This test is failing because the search is not returning the expected results +@skip("Fails randomly on CI") @test("route: search user docs") -def _(make_request=make_request, user=test_user, doc=test_user_doc): +async def _(make_request=make_request, user=test_user, doc=test_user_doc): + time.sleep(0.5) search_params = dict( text=doc.content[0], limit=1, @@ -173,8 +213,33 @@ def _(make_request=make_request, user=test_user, doc=test_user_doc): assert isinstance(docs, list) - # FIXME: This test is failing because the search is not returning the expected results - # assert len(docs) >= 1 + assert len(docs) >= 1 + + +@test("route: search agent docs hybrid with mmr") +async def _(make_request=make_request, agent=test_agent, doc=test_doc): + time.sleep(0.5) + + EMBEDDING_SIZE = 1024 + search_params = dict( + text=doc.content[0], + vector=[1.0] * EMBEDDING_SIZE, + mmr_strength=0.5, + limit=1, + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/search", + json=search_params, + ) + + assert response.status_code == 200 + response = response.json() + docs = response["docs"] + + assert isinstance(docs, list) + assert len(docs) >= 1 @test("routes: embed route") diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index 42904776d..9e75b3cda 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -3,10 +3,12 @@ from temporalio.client import WorkflowHandle from ward import test -from agents_api.autogen.Executions import ( +from agents_api.autogen.openapi_model import ( CreateExecutionRequest, + CreateTransitionRequest, + Execution, ) -from agents_api.autogen.openapi_model import CreateTransitionRequest, Execution +from agents_api.models.execution.count_executions import count_executions from agents_api.models.execution.create_execution import create_execution from agents_api.models.execution.create_execution_transition import ( create_execution_transition, @@ -90,6 +92,23 @@ def _( assert result[0].status == "queued" +@test("model: count executions") +def _( + client=cozo_client, + developer_id=test_developer_id, + execution=test_execution, + task=test_task, +): + result = count_executions( + developer_id=developer_id, + task_id=task.id, + client=client, + ) + + assert isinstance(result, dict) + assert result["count"] > 0 + + @test("model: create execution transition") def _(client=cozo_client, developer_id=test_developer_id, execution=test_execution): result = create_execution_transition( diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index c701a2e2f..dbfa2f8bd 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -1,12 +1,13 @@ # Tests for task queries import asyncio +import json from unittest.mock import patch import yaml from google.protobuf.json_format import MessageToDict from litellm.types.utils import Choices, ModelResponse -from ward import raises, test +from ward import raises, skip, test from agents_api.autogen.openapi_model import ( CreateExecutionRequest, @@ -559,6 +560,79 @@ async def _( assert result["hello"] == data.input["test"] +@test("workflow: tool call api_call test retry") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + status_codes_to_retry = ",".join(str(code) for code in (408, 429, 503, 504)) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "tools": [ + { + "type": "api_call", + "name": "hello", + "api_call": { + "method": "GET", + "url": f"https://httpbin.org/status/{status_codes_to_retry}", + }, + } + ], + "main": [ + { + "tool": "hello", + "arguments": { + "params": {"test": "_.test"}, + }, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + mock_run_task_execution_workflow.assert_called_once() + + # Let it run for a bit + result_coroutine = handle.result() + task = asyncio.create_task(result_coroutine) + try: + await asyncio.wait_for(task, timeout=10) + except BaseException: + task.cancel() + + # Get the history + history = await handle.fetch_history() + events = [MessageToDict(e) for e in history.events] + assert len(events) > 0 + + # NOTE: super janky but works + events_strings = [json.dumps(event) for event in events] + num_retries = len( + [event for event in events_strings if "execute_api_call" in event] + ) + + assert num_retries >= 2 + + @test("workflow: tool call integration dummy") async def _( client=cozo_client, @@ -612,6 +686,7 @@ async def _( assert result["test"] == data.input["test"] +@skip("integration service patch not working") @test("workflow: tool call integration mocked weather") async def _( client=cozo_client, @@ -671,8 +746,7 @@ async def _( assert result == expected_output -# FIXME: This test is not working. It gets stuck -# @test("workflow: wait for input step start") +@test("workflow: wait for input step start") async def _( client=cozo_client, developer_id=test_developer_id, @@ -710,7 +784,12 @@ async def _( mock_run_task_execution_workflow.assert_called_once() # Let it run for a bit - await asyncio.sleep(3) + result_coroutine = handle.result() + task = asyncio.create_task(result_coroutine) + try: + await asyncio.wait_for(task, timeout=3) + except asyncio.TimeoutError: + task.cancel() # Get the history history = await handle.fetch_history() @@ -728,12 +807,78 @@ async def _( activity for activity in activities_scheduled if activity ] - future = handle.result() - await future - assert "wait_for_input_step" in activities_scheduled +@test("workflow: foreach wait for input step start") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "foreach": { + "in": "'a b c'.split()", + "do": {"wait_for_input": {"info": {"hi": '"bye"'}}}, + }, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + # Let it run for a bit + result_coroutine = handle.result() + task = asyncio.create_task(result_coroutine) + try: + await asyncio.wait_for(task, timeout=3) + except asyncio.TimeoutError: + task.cancel() + + # Get the history + history = await handle.fetch_history() + events = [MessageToDict(e) for e in history.events] + assert len(events) > 0 + + activities_scheduled = [ + event.get("activityTaskScheduledEventAttributes", {}) + .get("activityType", {}) + .get("name") + for event in events + if "ACTIVITY_TASK_SCHEDULED" in event["eventType"] + ] + activities_scheduled = [ + activity for activity in activities_scheduled if activity + ] + + assert "for_each_step" in activities_scheduled + + @test("workflow: if-else step") async def _( client=cozo_client, @@ -749,9 +894,9 @@ async def _( "input_schema": {"type": "object", "additionalProperties": True}, "main": [ { - "if": "True", + "if": "False", "then": {"evaluate": {"hello": '"world"'}}, - "else": {"evaluate": {"hello": '"nope"'}}, + "else": {"evaluate": {"hello": "random.randint(0, 10)"}}, }, ], } @@ -779,7 +924,7 @@ async def _( mock_run_task_execution_workflow.assert_called_once() result = await handle.result() - assert result["hello"] == "world" + assert result["hello"] in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @test("workflow: switch step") @@ -988,6 +1133,62 @@ async def _( ] +@test("workflow: prompt step (python expression)") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + mock_model_response = ModelResponse( + id="fake_id", + choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], + created=0, + object="text_completion", + ) + + with patch("agents_api.clients.litellm.acompletion") as acompletion: + acompletion.return_value = mock_model_response + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "prompt": "$_ [{'role': 'user', 'content': _.test}]", + "settings": {}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + result = result["choices"][0]["message"] + assert result["content"] == "Hello, world!" + assert result["role"] == "assistant" + + @test("workflow: prompt step") async def _( client=cozo_client, diff --git a/agents-api/tests/test_files_queries.py b/agents-api/tests/test_files_queries.py new file mode 100644 index 000000000..712a083ca --- /dev/null +++ b/agents-api/tests/test_files_queries.py @@ -0,0 +1,57 @@ +# Tests for entry queries + + +from ward import test + +from agents_api.autogen.openapi_model import CreateFileRequest +from agents_api.models.files.create_file import create_file +from agents_api.models.files.delete_file import delete_file +from agents_api.models.files.get_file import get_file +from tests.fixtures import ( + cozo_client, + test_developer_id, + test_file, +) + + +@test("model: create file") +def _(client=cozo_client, developer_id=test_developer_id): + create_file( + developer_id=developer_id, + data=CreateFileRequest( + name="Hello", + description="World", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ), + client=client, + ) + + +@test("model: get file") +def _(client=cozo_client, file=test_file, developer_id=test_developer_id): + get_file( + developer_id=developer_id, + file_id=file.id, + client=client, + ) + + +@test("model: delete file") +def _(client=cozo_client, developer_id=test_developer_id): + file = create_file( + developer_id=developer_id, + data=CreateFileRequest( + name="Hello", + description="World", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ), + client=client, + ) + + delete_file( + developer_id=developer_id, + file_id=file.id, + client=client, + ) diff --git a/agents-api/tests/test_files_routes.py b/agents-api/tests/test_files_routes.py new file mode 100644 index 000000000..662612ff5 --- /dev/null +++ b/agents-api/tests/test_files_routes.py @@ -0,0 +1,88 @@ +import base64 +import hashlib + +from ward import test + +from tests.fixtures import make_request, s3_client + + +@test("route: create file") +async def _(make_request=make_request, s3_client=s3_client): + data = dict( + name="Test File", + description="This is a test file.", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ) + + response = make_request( + method="POST", + url="/files", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: delete file") +async def _(make_request=make_request, s3_client=s3_client): + data = dict( + name="Test File", + description="This is a test file.", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ) + + response = make_request( + method="POST", + url="/files", + json=data, + ) + + file_id = response.json()["id"] + + response = make_request( + method="DELETE", + url=f"/files/{file_id}", + ) + + assert response.status_code == 202 + + response = make_request( + method="GET", + url=f"/files/{file_id}", + ) + + assert response.status_code == 404 + + +@test("route: get file") +async def _(make_request=make_request, s3_client=s3_client): + data = dict( + name="Test File", + description="This is a test file.", + mime_type="text/plain", + content="eyJzYW1wbGUiOiAidGVzdCJ9", + ) + + response = make_request( + method="POST", + url="/files", + json=data, + ) + + file_id = response.json()["id"] + content_bytes = base64.b64decode(data["content"]) + expected_hash = hashlib.sha256(content_bytes).hexdigest() + + response = make_request( + method="GET", + url=f"/files/{file_id}", + ) + + assert response.status_code == 200 + + result = response.json() + + # Decode base64 content and compute its SHA-256 hash + assert result["hash"] == expected_hash diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index c0036d9b3..01fea1375 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -3,8 +3,12 @@ from ward import test -from agents_api.autogen.openapi_model import CreateOrUpdateSessionRequest, Session -from agents_api.autogen.Sessions import CreateSessionRequest +from agents_api.autogen.openapi_model import ( + CreateOrUpdateSessionRequest, + CreateSessionRequest, + Session, +) +from agents_api.models.session.count_sessions import count_sessions from agents_api.models.session.create_or_update_session import create_or_update_session from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session @@ -117,6 +121,17 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): assert len(result) > 0 +@test("model: count sessions") +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): + result = count_sessions( + developer_id=developer_id, + client=client, + ) + + assert isinstance(result, dict) + assert result["count"] > 0 + + @test("model: create or update session") def _( client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user diff --git a/agents-api/tests/test_sessions.py b/agents-api/tests/test_sessions.py new file mode 100644 index 000000000..b25a8a706 --- /dev/null +++ b/agents-api/tests/test_sessions.py @@ -0,0 +1,36 @@ +from ward import test + +from tests.fixtures import make_request + + +@test("model: list sessions") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/sessions", + ) + + assert response.status_code == 200 + response = response.json() + sessions = response["items"] + + assert isinstance(sessions, list) + assert len(sessions) > 0 + + +@test("model: list sessions with metadata filter") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/sessions", + params={ + "metadata_filter": {"test": "test"}, + }, + ) + + assert response.status_code == 200 + response = response.json() + sessions = response["items"] + + assert isinstance(sessions, list) + assert len(sessions) > 0 diff --git a/agents-api/tests/test_user_routes.py b/agents-api/tests/test_user_routes.py index 88f14d85a..229d85619 100644 --- a/agents-api/tests/test_user_routes.py +++ b/agents-api/tests/test_user_routes.py @@ -165,3 +165,21 @@ def _(make_request=make_request): assert isinstance(users, list) assert len(users) > 0 + + +@test("model: list users with right metadata filter") +def _(make_request=make_request, user=test_user): + response = make_request( + method="GET", + url="/users", + params={ + "metadata_filter": {"test": "test"}, + }, + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 diff --git a/agents-api/tests/test_workflow_routes.py b/agents-api/tests/test_workflow_routes.py index d1538535d..2ffc73173 100644 --- a/agents-api/tests/test_workflow_routes.py +++ b/agents-api/tests/test_workflow_routes.py @@ -46,6 +46,54 @@ async def _( @test("workflow route: evaluate step single with yaml") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + client, + ): + task_data = """ +name: test task +description: test task about +input_schema: + type: object + additionalProperties: true + +main: + - evaluate: + hello: '"world"' +""" + + result = ( + make_request( + method="POST", + url=f"/agents/{agent_id}/tasks", + content=task_data.encode("utf-8"), + headers={"Content-Type": "text/yaml"}, + ) + .raise_for_status() + .json() + ) + + task_id = result["id"] + + execution_data = dict(input={"test": "input"}) + + make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ).raise_for_status() + + +@test("workflow route: create or update: evaluate step single with yaml") async def _( cozo_client=cozo_client, developer_id=test_developer_id, diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index dc1007d13..4dfa69844 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -1,8 +1,10 @@ import asyncio +import io import logging from contextlib import asynccontextmanager, contextmanager from unittest.mock import patch +from botocore import exceptions from fastapi.testclient import TestClient from litellm.types.utils import ModelResponse from temporalio.testing import WorkflowEnvironment @@ -102,3 +104,53 @@ def patch_integration_service(output: dict = {"result": "ok"}): run_integration_service.return_value = output yield run_integration_service + + +@contextmanager +def patch_s3_client(): + class InMemoryS3Client: + def __init__(self): + self.store = {} + + def list_buckets(self): + return {"Buckets": [{"Name": bucket} for bucket in self.store.keys()]} + + def create_bucket(self, Bucket): + self.store[Bucket] = {} + + def head_object(self, Bucket, Key): + obj = self.store.get(Bucket, {}).get(Key) + + if obj is None: + raise exceptions.ClientError( + {"Error": {"Code": "404", "Message": "Not Found"}}, "HeadObject" + ) + + return obj + + def put_object(self, Bucket, Key, Body): + self.store[Bucket] = self.store.get(Bucket, {}) + self.store[Bucket][Key] = Body + + def get_object(self, Bucket, Key): + obj = self.store.get(Bucket, {}).get(Key) + + if obj is None: + raise exceptions.ClientError( + {"Error": {"Code": "404", "Message": "Not Found"}}, "GetObject" + ) + + file_io = io.BytesIO(obj) + + return {"Body": file_io} + + def delete_object(self, Bucket, Key): + self.store[Bucket] = self.store.get(Bucket, {}) + del self.store[Bucket][Key] + + in_memory_s3_client = InMemoryS3Client() + + with patch("agents_api.clients.s3.get_s3_client") as get_s3_client: + get_s3_client.return_value = in_memory_s3_client + + yield get_s3_client diff --git a/blob-store/.gitignore b/blob-store/.gitignore new file mode 100644 index 000000000..00bcc192e --- /dev/null +++ b/blob-store/.gitignore @@ -0,0 +1 @@ +/s3.json diff --git a/blob-store/Dockerfile b/blob-store/Dockerfile new file mode 100644 index 000000000..c36e8022c --- /dev/null +++ b/blob-store/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 +# check=error=true + +FROM chrislusf/seaweedfs + +# Install envsubst +ENV BUILD_DEPS="gettext" \ + RUNTIME_DEPS="libintl" + +RUN set -x && \ + apk add --update $RUNTIME_DEPS && \ + apk add --virtual build_deps $BUILD_DEPS && \ + cp /usr/bin/envsubst /usr/local/bin/envsubst && \ + apk del build_deps + +# Expected environment variables: +# - S3_ACCESS_KEY +# - S3_SECRET_KEY + +COPY s3.json.template /s3.json.template +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/blob-store/docker-compose-ha.yml b/blob-store/docker-compose-ha.yml new file mode 100644 index 000000000..6634357ea --- /dev/null +++ b/blob-store/docker-compose-ha.yml @@ -0,0 +1,82 @@ +name: julep-blob-store + +x-seaweedfs-base: + &seaweedfs-base + image: chrislusf/seaweedfs + profiles: + - blob-store + +services: + seaweedfs-master: + <<: *seaweedfs-base + ports: + - 9333:9333 + - 19333:19333 + command: "master -ip=seaweedfs-master -ip.bind=0.0.0.0 -port=9333 -metricsPort=9321 -raftBootstrap" + healthcheck: + test: [ "CMD", "wget", "-qSO", "-", "http://0.0.0.0:9333/cluster/healthz" ] + interval: 60s + retries: 6 + timeout: 60s + start_period: 30s + start_interval: 10s + + seaweedfs-volume: + <<: *seaweedfs-base + ports: + - 28080:28080 # Since 8080 is already used by agents-api, we use 28080 + - 18081:18080 + command: 'volume -mserver="seaweedfs-master:9333" -dir=/data -ip.bind=0.0.0.0 -port=28080 -ip=seaweedfs-volume -metricsPort=9321 -preStopSeconds=3' + healthcheck: + test: [ "CMD", "wget", "-qSO", "-", "http://0.0.0.0:28080/healthz" ] + interval: 60s + retries: 6 + timeout: 30s + start_period: 30s + start_interval: 10s + + depends_on: + seaweedfs-master: + condition: service_healthy + + volumes: + - seaweedfs_data:/data + + seaweedfs-filer: + <<: *seaweedfs-base + ports: + - 8888:8888 + - 18888:18888 + command: 'filer -master="seaweedfs-master:9333" -ip.bind=0.0.0.0 -port=8888 -ip=seaweedfs-filer -metricsPort=9321' + tty: true + stdin_open: true + healthcheck: + test: [ "CMD", "wget", "-qSO", "-", "http://0.0.0.0:8888/healthz" ] + interval: 60s + retries: 6 + timeout: 30s + start_period: 30s + start_interval: 10s + + depends_on: + seaweedfs-master: + condition: service_healthy + seaweedfs-volume: + condition: service_healthy + + seaweedfs-s3: + <<: *seaweedfs-base + ports: + - 8333:8333 + command: 's3 -filer="seaweedfs-filer:8888" -ip.bind=0.0.0.0 -port=8333 -metricsPort=9321' + depends_on: + seaweedfs-master: + condition: service_healthy + seaweedfs-volume: + condition: service_healthy + seaweedfs-filer: + condition: service_healthy + +volumes: + seaweedfs_data: + external: true diff --git a/blob-store/docker-compose.yml b/blob-store/docker-compose.yml new file mode 100644 index 000000000..089b31f39 --- /dev/null +++ b/blob-store/docker-compose.yml @@ -0,0 +1,39 @@ +name: julep-blob-store + +services: + seaweedfs: + image: julepai/blob-store:${TAG:-dev} + build: + context: . + dockerfile: Dockerfile + profiles: + - blob-store + + environment: + - S3_ACCESS_KEY=${S3_ACCESS_KEY} + - S3_SECRET_KEY=${S3_SECRET_KEY} + - DEBUG=${DEBUG:-true} + + ports: + - 9333:9333 # master port + - 8333:8333 # s3 port + - 8888:8888 # filer port + - 28080:28080 # volume port + # - 19333:19333 # master grpc port + # - 18081:18080 # volume grpc port + # - 18888:18888 # filer grpc port + command: "-volume -volume.dir.idx /data -filer -s3 -dir=/data -ip=seaweedfs -ip.bind=0.0.0.0 -metricsPort=9321 -master.raftBootstrap=false -master.port=9333 -volume.port=28080 -volume.index=leveldb -filer.port=8888 -s3.port=8333" + healthcheck: + test: [ "CMD", "wget", "-qSO", "-", "http://0.0.0.0:9333/cluster/healthz" ] + interval: 60s + retries: 6 + timeout: 60s + start_period: 30s + start_interval: 10s + + volumes: + - seaweedfs_data:/data + +volumes: + seaweedfs_data: + external: true diff --git a/blob-store/entrypoint.sh b/blob-store/entrypoint.sh new file mode 100755 index 000000000..156522249 --- /dev/null +++ b/blob-store/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -e + +# Check the environment variables +for var_name in S3_ACCESS_KEY S3_SECRET_KEY +do + if [ -z "`eval echo \\\$$var_name`" ]; then + echo "Error: Environment variable '$var_name' is not set." + exit 1 + fi +done + +# Generate the s3.json configuration file +envsubst < /s3.json.template > /s3.json + +if [ "$DEBUG" = "true" ]; then + echo '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' + echo '@@@ Careful: Debug mode is enabled. @@@' + echo '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' + + echo 'Printing s3.json:' + cat /s3.json +fi + +# Forward all arguments to the seaweedfs binary +exec weed server -s3.config=/s3.json "$@" diff --git a/blob-store/s3.json.template b/blob-store/s3.json.template new file mode 100644 index 000000000..e6c698a1f --- /dev/null +++ b/blob-store/s3.json.template @@ -0,0 +1,33 @@ +{ + "identities": [ + { + "name": "anonymous", + "actions": [ + "Read" + ] + }, + { + "name": "julep", + "credentials": [ + { + "accessKey": "${S3_ACCESS_KEY}", + "secretKey": "${S3_SECRET_KEY}" + } + ], + "actions": [ + "Admin", + "Read", + "List", + "Tagging", + "Write" + ] + } + ], + "accounts": [ + { + "id" : "julep", + "displayName": "Julep", + "emailAddress": "developers@julep.ai" + } + ] +} \ No newline at end of file diff --git a/code-interpreter/docker-compose.yml b/code-interpreter/docker-compose.yml new file mode 100644 index 000000000..96d4cae66 --- /dev/null +++ b/code-interpreter/docker-compose.yml @@ -0,0 +1,22 @@ +name: julep-code-interpreter + +services: + code-interpreter: + image: julepai/code-interpreter:${TAG:-dev} + + build: + context: ./vendor/cohere-ai/cohere-terrarium + dockerfile: Dockerfile + + volumes: + # See: https://github.com/cohere-ai/cohere-terrarium/blob/8df9169dac19199c9eb4091c232c2687d27bbc15/src/services/python-interpreter/service.ts#L9 + - code_interpreter_data:/usr/src/app/default_python_home + + profiles: + - code-interpreter + + ports: + - "9090:8080" # Binding to 9090, since 8080 is taken + +volumes: + code_interpreter_data: diff --git a/code-interpreter/vendor/cohere-ai/cohere-terrarium b/code-interpreter/vendor/cohere-ai/cohere-terrarium new file mode 160000 index 000000000..179d9cba1 --- /dev/null +++ b/code-interpreter/vendor/cohere-ai/cohere-terrarium @@ -0,0 +1 @@ +Subproject commit 179d9cba1fcd5c60573bed8391fbefdfb2a4a63c diff --git a/cookbooks/00-Devfest-Email-Assistant.ipynb b/cookbooks/00-Devfest-Email-Assistant.ipynb new file mode 100644 index 000000000..e69cbb624 --- /dev/null +++ b/cookbooks/00-Devfest-Email-Assistant.ipynb @@ -0,0 +1,482 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task Definition: Email Assistant\n", + "\n", + "### Overview\n", + "\n", + "This task creates an email assistant that can receive emails, search through Julep documentation, and respond to user inquiries automatically. It combines email integration with documentation search capabilities to provide relevant and informed responses.\n", + "\n", + "### Task Tools:\n", + "\n", + "**send_email**: An `integration` type tool that handles email sending via Mailgun SMTP.\n", + "**search_docs**: A `system` type tool that searches through agent documentation.\n", + "\n", + "### Task Input:\n", + "\n", + "A dictionary containing:\n", + "- **from**: Sender's email address\n", + "- **to**: Recipient's email address\n", + "- **subject**: Email subject\n", + "- **body**: Email content\n", + "\n", + "### Task Output:\n", + "\n", + "An email response sent to the inquirer with:\n", + "- Generated subject line\n", + "- Generated response body based on documentation search results\n", + "\n", + "### Task Flow\n", + "\n", + "1. **Input**: Receive email details (from, to, subject, body)\n", + "2. **Query Generation**: Generate a search query based on the email content\n", + "3. **Documentation Search**: Search Julep documentation using the generated query\n", + "4. **Response Generation**: Create a response using the found documentation\n", + "5. **Email Sending**: Send the response back to the original sender via Mailgun\n", + "\n", + "```plaintext\n", + "+----------+ +-------------+ +------------+ +-----------+\n", + "| Email | | Query | | Doc | | Email |\n", + "| Input | --> | Generation | --> | Search | --> | Response |\n", + "| (Query) | | | | | | Output |\n", + "+----------+ +-------------+ +------------+ +-----------+\n", + " | | | |\n", + " | | | |\n", + " v v v v\n", + " \"How do I\" Create search Find relevant \"Here's how to\n", + " \"use Julep?\" keywords documentation get started...\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: julep in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (1.6.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (4.6.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (0.27.2)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (2.9.2)\n", + "Requirement already satisfied: sniffio in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (1.3.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from julep) (4.12.2)\n", + "Requirement already satisfied: idna>=2.8 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from anyio<5,>=3.5.0->julep) (3.10)\n", + "Requirement already satisfied: certifi in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from httpx<1,>=0.23.0->julep) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from httpx<1,>=0.23.0->julep) (1.0.5)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->julep) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->julep) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->julep) (2.23.4)\n" + ] + } + ], + "source": [ + "!pip install julep" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import yaml\n", + "from julep import Julep\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "julep = Julep(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "agent = julep.agents.create(\n", + " name=\"Julep Email Assistant\",\n", + " about=(\n", + " \"You are an agent that handles emails for julep users.\"\n", + " + \" Julep is a platform for creating kick-ass AI agents.\"\n", + " ),\n", + " model=\"gpt-4o\",\n", + " default_settings={\"temperature\": 0.2},\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'231366f8-cdc8-423a-a1c6-72d4a300675f'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.id" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "mailgun_password = os.getenv(\"MAILGUN_PASSWORD\")\n", + "\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Julep Email Assistant\n", + "\n", + "input_schema:\n", + " type: object\n", + " properties:\n", + " from:\n", + " type: string\n", + " to:\n", + " type: string\n", + " subject:\n", + " type: string\n", + " body:\n", + " type: string\n", + "\n", + "tools:\n", + "- name: send_email\n", + " integration:\n", + " provider: email\n", + " setup:\n", + " host: smtp.mailgun.org\n", + " password: {mailgun_password}\n", + " port: 587\n", + " user: postmaster@email.julep.ai\n", + "\n", + "- name: search_docs\n", + " system:\n", + " resource: agent\n", + " subresource: doc\n", + " operation: search\n", + " \n", + "main:\n", + "- prompt: |-\n", + " You are {{{{ agent.name }}}}. {{{{ agent.about }}}}\n", + "\n", + " A user with email address {{{{ _.from }}}} has sent the following inquiry:\n", + " ------\n", + " Subject: {{{{ _.subject }}}}\n", + "\n", + " {{{{ _.body }}}}\n", + " ------\n", + "\n", + " Can you generate a query to search the documentation based on this email?\n", + " Just respond with the query as is and nothing else.\n", + "\n", + " unwrap: true\n", + "\n", + "- tool: search_docs\n", + " arguments:\n", + " agent_id: \"'{agent.id}'\"\n", + " text: _\n", + " \n", + "- prompt: |-\n", + " You are {{{{ agent.name }}}}. {{{{ agent.about }}}}\n", + "\n", + " A user with email address {{{{ inputs[0].from }}}} has sent the following inquiry:\n", + " ------\n", + " Subject: {{{{ inputs[0].subject }}}}\n", + "\n", + " {{{{ inputs[0].body }}}}\n", + " ------\n", + "\n", + " Here are some possibly relevant snippets from the julep documentation:\n", + " {{% for doc in _.docs %}}\n", + " {{% for snippet in doc.snippets %}}\n", + " {{{{ snippet.content }}}}\n", + " {{% endfor %}}\n", + " {{% endfor %}}\n", + " ========\n", + "\n", + " Based on the above info, craft an email body to respond with as a json object.\n", + " The json object must have `subject` and `body` fields.\n", + " response_format:\n", + " type: json_object\n", + " \n", + " unwrap: true\n", + " \n", + "- evaluate:\n", + " subject: \"load_json(_.split('```json')[1].split('```')[0])['subject']\"\n", + " body: \"load_json(_.split('```json')[1].split('```')[0])['body']\"\n", + " \n", + "- tool: send_email\n", + " arguments:\n", + " body: _.body\n", + " from: \"'postmaster@email.julep.ai'\"\n", + " subject: _.subject\n", + " to: inputs[0]['from']\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "task = julep.tasks.create(\n", + " agent_id=agent.id,\n", + " **task_def,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'a942a86d-dfcc-4abd-a8e7-0f502a2e4c67'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "task.id" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution\n", + "\n", + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "execution = julep.executions.create(\n", + " task_id=task.id,\n", + " input={\"from\": \"diwank@julep.ai\", \"to\": \"help@agents.new\", \"subject\": \"what's up\", \"body\": \"sup\"},\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Execution(id='54c1b4d2-c036-4b22-af8c-8c8a81fe41ad', created_at=datetime.datetime(2024, 10, 7, 14, 15, 15, 575516, tzinfo=datetime.timezone.utc), input={'body': 'sup', 'from': 'diwank@julep.ai', 'subject': \"what's up\", 'to': 'help@agents.new'}, status='running', task_id='a942a86d-dfcc-4abd-a8e7-0f502a2e4c67', updated_at=datetime.datetime(2024, 10, 7, 14, 15, 16, 717572, tzinfo=datetime.timezone.utc), error=None, metadata={}, output=None)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "julep.executions.get(execution.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output\n", + "\n", + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Type: init\n", + "output: {'body': 'sup', 'from': 'diwank@julep.ai', 'subject': \"what's up\", 'to': 'help@agents.new'}\n", + "----------------------------------------------------------------------------------------------------\n", + "Type: step\n", + "output: \"what's up\" site:julep.ai/docs\n", + "----------------------------------------------------------------------------------------------------\n", + "Type: step\n", + "output: {'docs': [], 'time': 0.007443666458129883}\n", + "----------------------------------------------------------------------------------------------------\n", + "Type: step\n", + "output: ```json\n", + "{\n", + " \"subject\": \"Hello!\",\n", + " \"body\": \"Hi there! How can I assist you today? If you have any questions or need help with Julep, feel free to let me know!\"\n", + "}\n", + "```\n", + "----------------------------------------------------------------------------------------------------\n", + "Type: step\n", + "output: {'body': 'Hi there! How can I assist you today? If you have any questions or need help with Julep, feel free to let me know!', 'subject': 'Hello!'}\n", + "----------------------------------------------------------------------------------------------------\n", + "Type: finish\n", + "output: {'success': True}\n", + "----------------------------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "execution_transitions = julep.executions.transitions.list(\n", + " execution_id=execution.id).items\n", + "\n", + "for transition in reversed(execution_transitions):\n", + " print(\"Type: \", transition.type)\n", + " print(\"output: \", transition.output)\n", + " print(\"-\" * 100)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cookbooks/01-Website_Crawler_using_Spider.ipynb b/cookbooks/01-Website_Crawler_using_Spider.ipynb deleted file mode 100644 index 36a77d525..000000000 --- a/cookbooks/01-Website_Crawler_using_Spider.ipynb +++ /dev/null @@ -1,322 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " \"julep\"\n", - "
\n", - "\n", - "## Task Definition: Spider Crawler Integration without Agents\n", - "\n", - "### Overview\n", - "\n", - "This task is designed to generate a simple Spider Crawler and integrate it with the workflow to scrape data from the web.\n", - "\n", - "### Task Flow\n", - "\n", - "1. **Input**: The user provides a URL to crawl.\n", - "\n", - "2. **Spider Tool Integration**: \n", - " - Create a spider tool that can crawl the web and extract data from the given URL.\n", - "\n", - "3. **Output**: The final output is the extracted data from the given URL.\n", - "\n", - "This task leverages the Spider Crawler tool to extract data from the given URL." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implementation\n", - "\n", - "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", - "\n", - "\n", - " \"Open\n", - "\n", - "\n", - "### Additional Information\n", - "\n", - "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", - "\n", - "**Author:** Julep AI \n", - "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Installing the Julep Client" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install julep -U --quiet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### NOTE:\n", - "\n", - "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", - "- Once created, these UUIDs should remain unchanged for simplicity.\n", - "- Altering a UUID will result in the system treating it as a new agent or task.\n", - "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Global UUID is generated for agent and task\n", - "import uuid\n", - "\n", - "AGENT_UUID = uuid.uuid4()\n", - "TASK_UUID = uuid.uuid4() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating Julep Client with the API Key" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from julep import Client\n", - "\n", - "api_key = \"\" # Your API key here\n", - "\n", - "# Create a client\n", - "client = Client(api_key=api_key, environment=\"dev\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an \"agent\"\n", - "\n", - "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", - "\n", - "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Defining the agent\n", - "name = \"Jarvis\"\n", - "about = \"The original AI conscious the Iron Man.\"\n", - "default_settings = {\n", - " \"temperature\": 0.7,\n", - " \"top_p\": 1,\n", - " \"min_p\": 0.01,\n", - " \"presence_penalty\": 0,\n", - " \"frequency_penalty\": 0,\n", - " \"length_penalty\": 1.0,\n", - " \"max_tokens\": 150,\n", - "}\n", - "\n", - "\n", - "# Create the agent\n", - "agent = client.agents.create_or_update(\n", - " agent_id=AGENT_UUID,\n", - " name=name,\n", - " about=about,\n", - " model=\"gpt-4o\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Defining a Task\n", - "\n", - "Tasks in Julep are Github Actions style workflows that define long-running, multi-step actions. \n", - "You can use them to conduct complex actions by defining them step-by-step. They have access to all Julep integrations.\n", - "\n", - "To learn more about tasks, visit [Julep documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#task)." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a Task which uses a agent to generate a sarcastic response to a given text using a DuckDuckGo search tool.\n", - "\n", - "More on how to define a task can be found [here](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# defining the task definition\n", - "task_def = yaml.safe_load(\"\"\"\n", - "name: Agent Crawler\n", - "\n", - "tools:\n", - "- name: spider_crawler\n", - " type: integration\n", - " integration:\n", - " provider: spider\n", - " setup:\n", - " spider_api_key: \"your_spider_api_key\"\n", - "\n", - "main:\n", - "- tool: spider_crawler\n", - " arguments:\n", - " url: '\"https://spider.cloud\"'\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating/Updating a task to generate a sarcastic response to a given text using a Intergation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# creating the task object\n", - "task = client.tasks.create_or_update(\n", - " task_id=TASK_UUID,\n", - " agent_id=AGENT_UUID,\n", - " **task_def\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an Execution\n", - "\n", - "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creates a execution worflow for the Task defined in the yaml file." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# creating an execution object\n", - "execution = client.executions.create(\n", - " task_id=TASK_UUID,\n", - " input={}\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# getting the execution details\n", - "execution = client.executions.get(execution.id)\n", - "#printing the output\n", - "execution.output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Retrieves and lists all the steps of a defined task that have been executed up to that point in time. Unlike streaming, this function does not continuously monitor the execution; it only provides a snapshot of the steps completed so far without displaying real-time updates as the task progresses." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.list(execution_id=execution.id).items" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Continuously monitor and stream the steps of a defined task. It retrieves and displays the transitions or execution steps of the task identified by execution.id in real-time, showing each step sequentially until the task is either completed or an error causes it to terminate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.stream(execution_id=execution.id)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "ai", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/cookbooks/01-website-crawler.ipynb b/cookbooks/01-website-crawler.ipynb new file mode 100644 index 000000000..5cba53da5 --- /dev/null +++ b/cookbooks/01-website-crawler.ipynb @@ -0,0 +1,557 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task Definition: Spider Crawler Integration\n", + "\n", + "### Overview\n", + "\n", + "This task is a simple task that leverages the spider `integration` tool, and combines it with a prompt step to crawl a website for a given URL, and then create a summary of the results.\n", + "\n", + "### Task Tools:\n", + "\n", + "**Spider Crawler**: An `integration` type tool that can crawl the web and extract data from a given URL.\n", + "\n", + "### Task Input:\n", + "\n", + "**url**: The URL of the website to crawl.\n", + "\n", + "### Task Output:\n", + "\n", + "**output**: A dictionary that contains a `documents` key which contains the extracted data from the given URL. Check the output below for a detailed output schema.\n", + "\n", + "### Task Flow\n", + "\n", + "1. **Input**: The user provides a URL to crawl.\n", + "\n", + "2. **Spider Tool Integration**: The `spider_crawler` tool is called to crawl the web and extract data from the given URL.\n", + "\n", + "3. **Prompt Step**: The prompt step is used to create a summary of the results from the spider tool.\n", + "\n", + "4. **Output**: The final output is the summary of the results from the spider tool.\n", + "\n", + "```plaintext\n", + "+----------+ +-------------+ +------------+ +-----------+\n", + "| User | | Spider | | Prompt | | Output |\n", + "| Input | --> | Crawler | --> | Step | --> | Step |\n", + "| (URL) | | | | | | Output |\n", + "+----------+ +-------------+ +------------+ +-----------+\n", + " | | | |\n", + " | | | |\n", + " v v v v\n", + " \"https://spider.cloud\" Extract data Create summary \"Here are the\n", + " from URL of results results from the\n", + " spider tool\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Installing the Julep Client" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --upgrade julep --quiet" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "\n", + "# NOTE: these UUIDs are used in order not to use the `create_or_update` methods instead of\n", + "# the `create` methods for the sake of not creating new resources every time a cell is run.\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a Julep client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Create agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=\"Spiderman\",\n", + " about=\"AI that can crawl the web and extract data\",\n", + " model=\"gpt-4o\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "\n", + "spider_api_key = os.getenv(\"SPIDER_API_KEY\")\n", + "\n", + "# Define the task\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Crawling Task\n", + "\n", + "# Define the tools that the agent will use in this workflow\n", + "tools:\n", + "- name: spider_crawler\n", + " type: integration\n", + " integration:\n", + " provider: spider\n", + " setup:\n", + " spider_api_key: \"{spider_api_key}\"\n", + "\n", + "# Define the steps of the workflow\n", + "main:\n", + "# Define a tool call step that calls the spider_crawler tool with the url input\n", + "- tool: spider_crawler\n", + " arguments:\n", + " url: \"_['url']\" # You can also use 'inputs[0]['url']'\n", + " \n", + " \n", + "- prompt: |\n", + " You are {{{{agent.about}}}}\n", + " I have given you this url: {{{{inputs[0]['url']}}}}\n", + " And you have crawled that website. Here are the results you found:\n", + " {{{{_['documents']}}}}\n", + " I want you to create a short summary (no longer than 100 words) of the results you found while crawling that website.\n", + "\n", + " unwrap: True\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# creating the task object\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# creating an execution object\n", + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"url\": \"https://spider.cloud\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spider.cloud is a leading web crawling tool designed for AI applications, offering high-speed, scalable, and cost-effective data collection solutions. Built in Rust, it can crawl over 20,000 pages in seconds, making it significantly faster and cheaper than traditional scrapers. Spider supports various data formats, including LLM-ready markdown, and integrates seamlessly with major AI tools. It offers features like auto proxy rotations, custom browser scripting, and caching to enhance performance. Users can start with $200 in credits and explore features through a free trial. Spider is trusted by tech businesses worldwide for insightful data solutions.\n" + ] + } + ], + "source": [ + "# Get execution details\n", + "execution = client.executions.get(execution.id)\n", + "# Print the output\n", + "print(execution.output)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition type: init\n", + "Transition output: {'url': 'https://spider.cloud'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'documents': [{'id': None, 'metadata': {'description': 'Experience cutting-edge web crawling with unparalleled speeds, perfect for LLMs, Machine Learning, and Artificial Intelligence. The fastest and most efficient web scraper tailored for AI applications.', 'domain': 'spider.cloud', 'extracted_data': None, 'file_size': 10031, 'keywords': ['AI agent stack', 'AWS infrastructure reduced', 'Auto Proxy rotations', 'Comprehensive Data Curation', 'Concurrent Streaming Save time', 'Data Collecting Projects Today Jumpstart web crawling', 'FAQ Frequently asked questions', 'Fastest Web Crawler', 'Latest sports news', 'Multiple response formats', 'Open Source Spider engine', 'Performance Tuned Spider', 'Seamless Integrations Seamlessly integrate Spider', 'Smart Mode Spider dynamically switches', 'Spider accurately crawls', 'Spider convert web data', 'Spider outputs HTML', 'Transform Convert raw HTML', 'WilliamEspegren Web crawler built', 'achieve crawling thousands', 'affordable web scraping', 'and`XML`for API responses', 'caching repeated web page crawls', 'cost step caching', 'crazy resource management Aaaaaaand', 'custom browser scripting', 'data = json', 'data formats including LLM-ready markdown', 'ensure continuous maintenance', 'ensuring data curation perfectly aligned', 'finest data collecting solution', 'full elastic scaling concurrency', 'handle extreme workloads', 'iammerrick Rust based crawler Spider', 'insightful data solutions', 'json headers =', 'large scraping projects', 'large-scale data collection', 'leading tech businesses worldwide', 'leading web crawling tool designed', 'real-time web data', 'request Python JSONL Copy ``` `import requests', 'requires JavaScript rendering', 'response = requests', 'robust Rust engine scales effortlessly', 'scrapes significantly faster', 'search engine results', 'traditional scraping services Spider API Request Modes', 'training AI models', 'user interface segment showing'], 'pathname': '/', 'resource_type': '.md', 'title': 'Spider: The Web Crawler for AI', 'url': '8475428e-4e0c-44de-967f-c14fb73cf490/spider.cloud/_cloud/12638123428881205758.md', 'user_id': '8475428e-4e0c-44de-967f-c14fb73cf490'}, 'page_content': \"To help you get started with Spider, we’ll give you $200 in credits when you spend $100.[Terms apply](https://spider.cloud/promotion-spider-credits)\\n# The Web Crawler for AI Agents and LLMs\\nSpider offers the finest data collecting solution. Engineered for speed and scalability, it\\nallows you to elevate your AI projects.\\n[Get Started](https://spider.cloud/credits/new)View Preview\\n* Basic\\n* Streaming\\nExample request\\nPython\\nJSONL\\nCopy\\n```\\n`import requests, os, json\\nheaders = {\\n''Authorization'': f''Bearer {os.getenv(""SPIDER\\\\_API\\\\_KEY"")}'',\\n''Content-Type'': ''application/jsonl'',\\n}\\njson\\\\_data = {""limit"":50,""metadata"":True,""url"":""https://spider.cloud""}\\nresponse = requests.post(''https://api.spider.cloud/crawl'', headers=headers, json=json\\\\_data, stream=True)\\nwith response as r:\\nr.raise\\\\_for\\\\_status()\\nfor chunk in r.iter\\\\_lines(\\nchunk\\\\_size=None, decode\\\\_unicode=True\\n):\\ndata = json.loads(chunk)\\nprint(data)`\\n```\\n[Free Trial](https://spider.cloud/credits/new?free-trial=1)\\nExample Response\\n## Built with the need for**Speed**\\nExperience the power of**Spider**, built fully in**Rust**for\\nnext-generation scalability.\\n### 2secs\\nCapable of crawling over 20k pages in batch mode\\n### 500-1000x\\nFaster than alternatives\\n### 500x\\nCheaper than traditional scraping services\\nSpider API Request Modes · Benchmarked tailwindcss.com ·06/16/2024\\n[See framework benchmarks](https://github.com/spider-rs/spider/blob/main/benches/BENCHMARKS.md)\\n### Seamless Integrations\\nSeamlessly integrate Spider with a wide range of platforms, ensuring data curation\\nperfectly aligned with your requirements. Compatible with all major AI tools.\\n[LangChain integration](https://python.langchain.com/docs/integrations/document_loaders/spider)[LlamaIndex integration](https://docs.llamaindex.ai/en/stable/examples/data_connectors/WebPageDemo/#using-spider-reader)[CrewAI integration](https://docs.crewai.com/tools/SpiderTool/)[FlowWiseAI integration](https://docs.flowiseai.com/integrations/langchain/document-loaders/spider-web-scraper-crawler)[Composio integration](https://docs.composio.dev/introduction/foundations/components/list_local_tools#spider-crawler)[PhiData integration](https://docs.phidata.com/tools/spider)\\n### Concurrent Streaming\\nSave time and money without having to worry about bandwidth concerns by effectively\\nstreaming all the results concurrently. The latency cost that is saved becomes drastic as\\nyou crawl more websites.\\n### Warp Speed\\nPowered by the cutting-edge[Spider](https://github.com/spider-rs/spider)open-source project, our robust Rust engine scales effortlessly to handle extreme\\nworkloads. We ensure continuous maintenance and improvement for top-tier performance.\\n## Kickstart Your Data Collecting Projects Today\\nJumpstart web crawling with full elastic scaling concurrency, optimal formats, and AI scraping.\\n### Performance Tuned\\nSpider is written in Rust and runs in full concurrency to achieve crawling thousands of\\npages in secs.\\n### Multiple response formats\\nGet clean and formatted markdown, HTML, or text content for fine-tuning or training AI\\nmodels.\\n### Caching\\nFurther boost speed by caching repeated web page crawls to minimize expenses while\\nbuilding.\\n### Smart Mode\\nSpider dynamically switches to Headless Chrome when it needs to quick.\\nBeta\\n### Scrape with AI\\nDo custom browser scripting and data extraction using the latest AI models with no cost\\nstep caching.\\n### The crawler for LLMs\\nDon't let crawling and scraping be the highest latency in your LLM & AI agent stack.\\n### Scrape with no headaches\\n* Auto Proxy rotations\\n* Agent headers\\n* Anti-bot detections\\n* Headless chrome\\n* Markdown responses\\n### The Fastest Web Crawler\\n* Powered by[spider-rs](https://github.com/spider-rs/spider)\\n* 100,000 pages/seconds\\n* Unlimited concurrency\\n* Simple API\\n* 50,000 RPM\\n### Do more with AI\\n* Browser scripting\\n* Advanced extraction\\n* Data pipelines\\n* Ideal for LLMs and AI Agents\\n* Accurate labeling\\n## Achieve more with these new API features\\nOur API is set to stream so you can act in realtime.\\n![A user interface with a search bar containing the text "Latest sports news," a green "Submit" button, and two icon buttons to display searching and extracting with the service.](https://spider.cloud/img/search_feature.webp)\\n### Search\\nGet access to search engine results from anywhere and easily crawl and transform pages to\\nLLM-ready markdown.\\n[Explore Search](https://spider.cloud/docs/api#search)\\n![A user interface segment showing three icons representing different stages of data transformation.](https://spider.cloud/img/transform_feature_example.webp)\\n### Transform\\nConvert raw HTML into markdown easily by using this API. Transform thousands of html pages\\nin seconds.\\n[Explore Transform](https://spider.cloud/docs/api#transform)\\n## Join the community\\nBacked by a network of early advocates, contributors, and supporters.\\n[GitHub discussions\\n](https://github.com/orgs/spider-rs/discussions)[Discord\\n](https://discord.spider.cloud)\\n[\\n![iammerrick's avatar](https://spider.cloud/img/external/iammerrick_twitter.webp)\\n@iammerrick\\nRust based crawler Spider is next level for crawling & scraping sites. So fast.\\nTheir cloud offering is also so easy to use. Good stuff. https://github.com/spider-rs/spider\\n](https://twitter.com/iammerrick/status/1787873425446572462)\\n[\\n![WilliamEspegren's avatar](https://spider.cloud/img/external/william_twitter.webp)\\n@WilliamEspegren\\nWeb crawler built in rust, currently the nr1 performance in the world with crazy resource management Aaaaaaand they have a cloud offer, that’s wayyyy cheaper than any competitor\\nName a reason for me to use anything else?\\ngithub.com/spider-rs/spid…\\n](https://twitter.com/WilliamEspegren/status/1789419820821184764)\\n[\\n![gasa's avatar](https://spider.cloud/img/external/gaza_twitter.webp)\\n@gasa\\n@gasathenaper\\nis the best crawling tool i have used. I had a complicated project where i needed to paste url and get the website whole website data. Spider does it in an instant\\n](https://x.com/gasathenaper/status/1810612492596383948)\\n[\\n![Ashpreet Bedi's avatar](https://spider.cloud/img/external/ashpreet_bedi.webp)\\n@Ashpreet Bedi\\n@ashpreetbedi\\nis THE best crawler out there, give it a try\\n](https://x.com/ashpreetbedi/status/1815512219003572315?s=46&t=37F5QP_8oKqOsNpHSo6VVw)\\n[\\n![Troyusrex's avatar](https://spider.cloud/img/external/troy_twitter.webp)\\n@Troyusrex\\nI found a new tool, Spider-rs, which scrapes significantly faster and handles more scenarios than the basic scraper I built did. Our use of Spider-rs and AWS infrastructure reduced the scraping time from four months to under a week.\\n](https://medium.com/@troyusrex/inside-my-virtual-college-advisor-a-deep-dive-into-rag-ai-and-agent-technology-84731b2928f7#1326)\\n[\\n![Dify.AI's avatar](https://spider.cloud/img/external/difyai.webp)\\n@Dify.AI\\n🕷️Spider @spider\\\\_rust\\ncan be used as a built-in tool in #Dify Workflow or as an LLM-callable tool in Agent. It allows fast and affordable web scraping and crawling when your AI applications need real-time web data for context.\\n](https://x.com/dify_ai/status/1818226971056243089)\\n## FAQ\\nFrequently asked questions about Spider.\\n### What is Spider?\\nSpider is a leading web crawling tool designed for speed and cost-effectiveness, supporting various data formats including LLM-ready markdown.\\n### Why is my website not crawling?\\nYour crawl may fail if it requires JavaScript rendering. Try setting your request to 'chrome' to solve this issue.\\n### Can you crawl all pages?\\nYes, Spider accurately crawls all necessary content without needing a sitemap.\\n### What formats can Spider convert web data into?\\nSpider outputs HTML, raw, text, and various markdown formats. It supports`JSON`,`JSONL`,`CSV`, and`XML`for API responses.\\n### Is Spider suitable for large scraping projects?\\nAbsolutely, Spider is ideal for large-scale data collection and offers a cost-effective dashboard for data management.\\n### How can I try Spider?\\nPurchase credits for our cloud system or test the Open Source Spider engine to explore its capabilities.\\n### Does it respect robots.txt?\\nYes, compliance with robots.txt is default, but you can disable this if necessary.\\n### Unable to get dynamic content?\\nIf you are having trouble getting dynamic pages, try setting the request parameter to ""chrome"" or ""smart."" You may also need to set `disable\\\\_intercept` to allow third-party or external scripts to run.\\n### Why is my crawl going slow?\\nIf you are experiencing a slow crawl, it is most likely due to the robots.txt file for the website. The robots.txt file may have a crawl delay set, and we respect the delay up to 60 seconds.\\n### Do you offer a Free Trial?\\nYes, you can try out the service before being charged for free at[checkout](https://spider.cloud/credits/new?free-trial=1).\\n## Comprehensive Data Curation for Everyone\\nTrusted by leading tech businesses worldwide to deliver accurate and insightful data solutions.\\n[Zapier](https://zapier.com/apps/spider/integrations)\\n### Next generation data for AI, scale to millions\\n[Start now](https://spider.cloud/credits/new)\\n### Company\\n* [About](https://spider.cloud/about)\\n* [Privacy](https://spider.cloud/privacy)\\n* [Terms](https://spider.cloud/eula)\\n* [FAQ](https://spider.cloud/faq)\\n### Resources\\n* [API](https://spider.cloud/docs/api)\\n* [Docs](https://spider.cloud/docs/overview)\\n* [Guides](https://spider.cloud/guides)\\n* [Spider.rs Docs](https://docs.rs/spider/latest/spider/)\\n### Services\\n* [Pricing](https://spider.cloud/credits/new)\\n* [Web Crawling and Scraping](https://spider.cloud/web-crawling-and-scraping)\\n[All systems normal.](https://spidercloud.statuspage.io/)\\n[\\nGitHub\\n](https://github.com/spider-rs/spider)[\\nDiscord\\n](https://discord.spider.cloud)[\\nTwitter\\n](https://twitter.com/spider_rust)\", 'type': 'Document'}]}\n", + "--------------------------------------------------\n", + "Transition type: finish\n", + "Transition output: Spider.cloud is a leading web crawling tool designed for AI applications, offering high-speed, scalable, and cost-effective data collection solutions. Built in Rust, it can crawl over 20,000 pages in seconds, making it significantly faster and cheaper than traditional scrapers. Spider supports various data formats, including LLM-ready markdown, and integrates seamlessly with major AI tools. It offers features like auto proxy rotations, custom browser scripting, and caching to enhance performance. Users can start with $200 in credits and explore features through a free trial. Spider is trusted by tech businesses worldwide for insightful data solutions.\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running the same task with a different URL" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use the same code to run the same task, but with a different URL" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"url\": \"https://www.harvard.edu/\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Harvard University's website emphasizes its commitment to excellence in teaching, learning, and research\n", + "It highlights initiatives related to food, including nutrition, sustainability, and healthful eating\n", + "The site features experts like Christina Warinner and Leah Penniman, and initiatives like the Harvard Food Systems Initiative and Food Literacy Project\n", + "It explores topics such as junk food cravings, vegan diets, and the impact of avocados on heart disease\n", + "The site also showcases Harvard's efforts in sustainable food practices, food donation programs, and educational resources like free online cooking courses\n", + "Additionally, it highlights the contributions of chefs within the Harvard community.\n" + ] + } + ], + "source": [ + "execution = client.executions.get(execution.id)\n", + "print(\"\\n\".join(execution.output.split(\". \")))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: you can get the output of the crawling step by accessing the corresponding transition's output from the transitions list.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'documents': [{'id': None,\n", + " 'metadata': {'description': 'Harvard University is devoted to excellence in teaching, learning, and research, and to developing leaders who make a difference globally.',\n", + " 'domain': 'www.harvard.edu',\n", + " 'extracted_data': None,\n", + " 'file_size': 15310,\n", + " 'keywords': ['Business School podcast',\n", + " 'Christina Warinner Christina co-authored',\n", + " 'Dental Medicine shares advice',\n", + " 'Dining Services Learnings Report Learn',\n", + " 'Dining Services team',\n", + " 'Education alum started Bite Sized Education',\n", + " 'Expand Image Joanne Chang Stephanie Mitchell',\n", + " 'Expand Image Julia Child Paul Child Julia Child',\n", + " 'Expand Image Ludger Wessels Chef Wessels',\n", + " 'Expand Image Nick DiGiovanni Kris Snibbe',\n", + " 'Expand Image Nisha Vora Photo',\n", + " 'Flour Bakery owner Joanne Chang',\n", + " 'Food Donation Program',\n", + " 'Food Food nourishes',\n", + " 'Food Law',\n", + " 'Food Literacy Project',\n", + " 'Food Literacy Project hosts',\n", + " 'Free cooking courses Learn',\n", + " 'Graduate School',\n", + " 'Harvard Chan School',\n", + " 'Harvard Chan School found',\n", + " 'Harvard College first-year student explains',\n", + " 'Harvard College student',\n", + " 'Harvard College student stories',\n", + " 'Harvard College student story',\n", + " 'Harvard Divinity School explores',\n", + " 'Harvard Food Systems Initiative',\n", + " 'Harvard Food Systems Initiative Led',\n", + " 'Harvard Square food scene',\n", + " 'Harvard Staff Photographer',\n", + " 'Harvard University Dining Services',\n", + " 'Harvard University Dining Services launched',\n", + " 'Harvard alum',\n", + " 'Harvard alum Marcos Barrozo worked',\n", + " 'Healthful Food Standards',\n", + " 'Healthy Eating Plate',\n", + " 'Historian Joyce Chaplin explored',\n", + " 'Ingestible insights Harvard researchers',\n", + " 'James Beard Leadership Award winner',\n", + " 'Joanne Chang wears',\n", + " 'Julia Child pours hot syrup',\n", + " 'Leah Gose worked',\n", + " 'Leah Penniman Leah',\n", + " 'Luke MacQueen spent years creating',\n", + " 'Michael Marquand Law School grad Nisha Vora left',\n", + " 'Prevention Research Center',\n", + " 'Sustainable Food Harvard',\n", + " 'Sustainable Food Systems Graduate Certificate',\n", + " 'Teaching science concepts',\n", + " 'accelerating food shortages',\n", + " 'ancient humans adapted',\n", + " 'biggest problems facing farmers',\n", + " 'chef Ludger Wessels',\n", + " 'co-founded Soul Fire Farm',\n", + " 'converting food waste',\n", + " 'crave junk food',\n", + " 'crave junk food Harvard Medical School',\n", + " 'cultivate healthier eating habits',\n", + " 'engage secondary students',\n", + " 'exploring medical technology',\n", + " 'food Graduate School alum John Ahrens cultivated',\n", + " 'food Research shows',\n", + " 'food connoisseur shares',\n", + " 'food insecurity solutions',\n", + " 'free online courses',\n", + " 'gas fermentation-derived chocolate',\n", + " 'heart disease risk',\n", + " 'inspire elevated thinking',\n", + " 'lab-grown fish fillets',\n", + " 'leading authority providing science-based guidance',\n", + " 'local nonprofit Food',\n", + " 'peach industry fight brown rot',\n", + " 'postmenopausal weight loss',\n", + " 'reduce food waste',\n", + " 'reduce greenhouse gas emissions',\n", + " 'regenerative farming practices',\n", + " 'seasonal rainfall patterns',\n", + " 'selling meat-free meals',\n", + " 'shape future food systems leaders',\n", + " 'six-time American Culinary Federation competition award winner',\n", + " 'specialties include regional German',\n", + " 'vegetarian restaurant Clover'],\n", + " 'pathname': '/',\n", + " 'resource_type': '.md',\n", + " 'title': 'Harvard University',\n", + " 'url': '8475428e-4e0c-44de-967f-c14fb73cf490/www.harvard.edu/www_edu/12638123428881205758.md',\n", + " 'user_id': '8475428e-4e0c-44de-967f-c14fb73cf490'},\n", + " 'page_content': '[Skip to main content](#main-content)\\n[Harvard University](https://www.harvard.edu/)\\nSearch\\nMenu\\n[Harvard University](https://www.harvard.edu/)\\nClose\\n# Harvard University\\n## Food\\nFood nourishes us, inspires us, and brings us together. The Harvard community is exploring nutrition, sustainability, and the science behind the things we eat.\\n## [Tips for your teeth](https://hsdm.harvard.edu/news/maintaining-your-oral-health-during-holiday-season)\\nThe Harvard School of Dental Medicine shares advice for keeping your teeth healthy between holiday meals.\\n![]()\\n## Experts in the edible\\n### Christina Warinner\\nChristina co-authored a study showing that ancient humans adapted to eating starch-rich foods as far back as 100,000 years ago. These foods likely helped pave the way for the expansion of the human brain.\\nLearn more about the study\\n[Learn more about the study](https://news.harvard.edu/gazette/story/2021/05/study-explains-early-humans-ate-starch-and-why-it-matters/)\\n### Ayr Muir\\nAs the founder of the vegetarian restaurant Clover, Ayr is selling meat-free meals and reducing our impact on climate change one sandwich at a time.\\nExplore the Business School podcast\\n[Explore the Business School podcast](https://www.alumni.hbs.edu/stories/Pages/story-bulletin.aspx?num=7302)\\n### Leah Penniman\\nLeah, a James Beard Leadership Award winner, co-founded Soul Fire Farm to train people in regenerative farming practices that are now the go-to methods of sustainable and organic agriculture.\\nLearn more about her farm\\n[Learn more about her farm](https://hds.harvard.edu/news/2019/09/18/leah-penniman-fight-food-justice)\\n![](https://www.harvard.edu/wp-content/themes/core/assets/img/theme/shims/16x9.png)\\nTeaching science concepts through cooking\\n[Click to Play Video](https://www.youtube.com/watch?v=cOJYELbyYGg)\\n### Kate Strangfeld\\nThe Graduate School of Education alum started Bite Sized Education to engage secondary students in science. Her goal is to empower students to “think like a scientist” through food and cooking.\\n## Ingestible insights\\nHarvard researchers are exploring the effects that certain foods and types of eating have on our health.\\n[Learn more about what you should eat](https://nutritionsource.hsph.harvard.edu/what-should-you-eat/)\\n![Two cartoon heads, one with a soda inside it and one with a broccoli](https://www.harvard.edu/wp-content/uploads/2024/11/this_or_that-junk-food-craving-wondering2.png?w=736&h=491&crop=1)### Why we crave junk food\\nHarvard Medical School’s Uma Naidoo, the author of the books “This Is Your Brain on Food” and “Calm Your Mind with Food,” explains why we crave junk food and how to cultivate healthier eating habits.\\n[Why we crave junk food](https://news.harvard.edu/gazette/story/2024/09/why-do-we-crave-junk-food-diet-psychology/)\\n### Chocolate\\nand its effect on postmenopausal weight loss\\n![A bar of chocolate](https://www.harvard.edu/wp-content/uploads/2024/11/chocolate-1277002_1280.jpg?w=375&h=281&crop=1)[Chocolate](https://news.harvard.edu/gazette/story/2021/06/starting-the-day-off-with-chocolate-may-have-unexpected-benefits/)\\n### Ultra-processed food\\nand its influence on obesity and disease\\n![Nuggets and fries](https://www.harvard.edu/wp-content/uploads/2024/11/chicken-nuggets-246180_1280.jpg?w=375&h=281&crop=1)[Ultra-processed food](https://news.harvard.edu/gazette/story/2023/12/why-are-americans-so-sick-researchers-point-to-middle-grocery-aisles/)\\n### Vegan diet\\nand Alzheimer’s improvements\\n![A colorful salad](https://www.harvard.edu/wp-content/uploads/2024/11/food-1075228_1280.jpg?w=375&h=281&crop=1)[Vegan diet](https://news.harvard.edu/gazette/story/2024/07/alzheimers-study-finds-diet-lifestyle-changes-yield-improvements/)\\n### Avocados\\nand their effect on heart disease risk\\n![avocados](https://www.harvard.edu/wp-content/uploads/2024/11/avocado-8498520_1280.jpg?w=375&h=281&crop=1)[Avocados](https://news.harvard.edu/gazette/story/2022/04/an-avocado-a-week-may-lower-heart-disease-risk/)\\n### Late-night eating\\nand its role in depression and mood\\n![A person looking into a fridge at night](https://www.harvard.edu/wp-content/uploads/2024/11/AdobeStock_254026165.jpeg?w=375&h=281&crop=1)[Late-night eating](https://hms.harvard.edu/news/daytime-eating-mental-health)\\nHome cooking\\n## Chefs in our community\\n[Explore some of the amazing dishes from chefs all over Harvard](https://www.harvard.edu/in-focus/food/recipes/)\\nMeet the chefs\\n![Joanne Chang wears an apron in a kitchen](https://www.harvard.edu/wp-content/uploads/2024/11/020320_Chang_Joanne_09b.jpg?w=731)\\nExpand Image\\nJoanne Chang\\nStephanie Mitchell/Harvard Staff Photographer\\n"Pastry Love," a cookbook by Harvard alum and Flour Bakery owner Joanne Chang, honors some of her decadent and delicious favorites.\\n[Explore one of Joanne's recipes](https://www.harvard.edu/in-focus/food/recipes#joanne)\\n![A student wearing an apron stands in a kitchen with a cutting board of purple cabbage in front of him](https://www.harvard.edu/wp-content/uploads/2022/10/062629_Chef_006-e1675280860455.jpg?w=1200)\\nExpand Image\\nNick DiGiovanni\\nKris Snibbe/Harvard Staff Photographer\\nWhile at Harvard College, Nick DiGiovanni, who competed on MasterChef, created his own concentration in food and climate.\\n[Explore one of Nick's recipe](https://www.harvard.edu/in-focus/food/recipes#nick)\\n![Julia Child pours hot syrup over a Croquembouche in a kitchen](https://www.harvard.edu/wp-content/uploads/2022/10/Screen-Shot-2022-10-25-at-8.49.09-AM.png?w=634)\\nExpand Image\\nJulia Child\\nPaul Child\\nJulia Child’s television series “The French Chef,” which first aired in 1963, launched a revolution in cooking and eating in the United States. Her papers are part of the collection at the Schlesinger Library.\\n[Explore one of Julia's recipes](https://www.harvard.edu/in-focus/food/recipes#julia)\\n![A woman stands in a kitchen](https://www.harvard.edu/wp-content/uploads/2022/10/Nisha_Portrait_2500-copy-e1675280779442.jpg?w=1500)\\nExpand Image\\nNisha Vora\\nPhoto by Michael Marquand\\nLaw School grad Nisha Vora left the legal life for vegan cooking.\\n[Explore one of Nisha's recipes](https://www.harvard.edu/in-focus/food/recipes#nisha)\\n![Headshot of chef Ludger Wessels against a brick wall](https://www.harvard.edu/wp-content/uploads/2022/10/LudgerWessels-e1675281023551.jpg?w=1600)\\nExpand Image\\nLudger Wessels\\nChef Wessels, a six-time American Culinary Federation competition award winner, is executive chef at Harvard University Dining Services. His specialties include regional German and French cuisine.\\n[Explore one of Ludger's recipes](https://www.harvard.edu/in-focus/food/recipes#ludger)\\n## Further into food\\n### The Nutrition Source\\nA leading authority providing science-based guidance on food and nutrition. Explore articles, recipes, and tools.\\nExplore the resources\\n[Explore the resources](https://nutritionsource.hsph.harvard.edu/)\\n### Free cooking courses\\nLearn about fermentation or explore the chemistry and physics of cooking in these free online courses.\\nSign up for a free cooking course\\n[Sign up for a free cooking course](https://pll.harvard.edu/catalog?keywords=cooking)\\n### The sacredness of food\\nThe Harvard Divinity School explores the many ways that food overlaps with religions around the world.\\nLearn more from the Divinity School\\n[Learn more from the Divinity School](https://news-archive.hds.harvard.edu/news/2016/11/22/sacredness-food)\\n## Sustenance solutions\\nThe Harvard community is exploring solutions for the biggest problems facing farmers, consumers, and the planet.\\n### The need for food\\nReserachers at Harvard Chan School found an[increase in food insufficiency](https://www.hsph.harvard.edu/news/press-releases/)following the decrease in SNAP benefits; Graduate School of Design[students looked at food insecurity solutions in Mississippi](https://news.harvard.edu/gazette/story/newsplus/designing-food-security-in-rural-mississippi/), ranked as America’s hungriest state; Harvard alum[Leah Gose worked to improve how communities respond to hunger](https://gsas.harvard.edu/news/hungry-change), starting with her hometown of Atlanta; and Historian Joyce Chaplin explored[the long history of who gets to eat](https://hmsc.harvard.edu/2020/07/01/food-and-status-a-discussion-with-historian-joyce-chaplin/).\\n### The risks for food\\nResearch shows that changes to seasonal rainfall patterns are[accelerating food shortages](https://salatainstitute.harvard.edu/interview-unpredictable-rains-erode-food-security-gains/)across the globe; Harvard alum Marcos Barrozo worked to[lower emissions from Brazil’s cattle industry](https://gsas.harvard.edu/news/beef-climate-change); the Wyss Institute is exploring medical technology to[help the peach industry fight brown rot](https://wyss.harvard.edu/media-post/arbor-armor-could-fcmbl-save-the-peach-industry/?q=arbor); and the Schools of Engineering and Public Health are teaming up to[reduce food waste and foodborne illness](https://seas.harvard.edu/news/2022/06/food-packaging-system-reduces-health-risks-and-saves-food)in a novel way.\\n### The future of food\\nGraduate School alum John Ahrens cultivated[lab-grown fish fillets](https://gsas.harvard.edu/news/reel-hope-ocean-conservation)to address the global demand for seafood; Wyss Institute’s Luke MacQueen spent years creating[an animal-free meat that tastes and feels like the real thing](https://wyss.harvard.edu/news/from-montreal-to-a-menu-near-you/); the founders of Circe created the[world’s first gas fermentation-derived chocolate](https://otd.harvard.edu/news/circe-bioscience-licenses-technology-to-decarbonize-industry-with-microbes-developed-at-wyss-institute-at-harvard-university/)in response to the worldwide shortage of cocoa; and David Weitz’s lab is exploring[converting food waste into sugar alternatives](https://salatainstitute.harvard.edu/salata-institute-funds-five-new-climate-research-projects/).\\n## Food at Harvard\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/082922_Global_158-1-1168x934-1.jpg?w=500&h=500&crop=1)\\n### Healthful and Sustainable Food\\nHarvard's Office for Sustainability pledges to reduce greenhouse gas emissions from food by 25% by 2030 as part of the Coolfood Pledge.\\n[Learn more about Harvard's sustainable and healthful food](https://sustainable.harvard.edu/our-plan/how-we-operate/food/)\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/Family-Meals-before-and-after-768x639b.jpg?w=500&h=500&crop=1)\\n### Food Donation Program\\nIn 2014, Harvard University Dining Services launched an effort to address chronic hunger among its neighbors in Cambridge and Boston by partnering with the local nonprofit Food for Free to donate nearly 2,000 nutritious meals each week to families in need.\\n[Learn more about the program](https://dining.harvard.edu/about-huds/sustainability/food-donation-program)\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/Annenberg_0.jpeg?w=500&h=500&crop=1)\\n### Finding home through food\\nA Harvard College first-year student explains what the food is like at Harvard, and how to cure food-homesickness.\\n[Learn more from Harvard College student stories](https://college.harvard.edu/student-life/student-stories/finding-home-through-food)\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/MG_5399-1-1024x683-1.jpg?w=500&h=500&crop=1)\\n### Harvard Food Systems Initiative\\nLed by Harvard University Dining Services in collaboration with Harvard Faculty and practitioners in the field, Harvard Food Systems Initiative is an educational program to inspire elevated thinking and change to shape future food systems leaders for a more sustainable future.\\n[Learn more about the initiative](https://hfsi.harvard.edu/)\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/img-2670_1-1.jpg?w=500&h=500&crop=1)\\n### The Harvard Square food scene\\nA Harvard College student and food connoisseur shares the best places to grab a bite in the square.\\n[Learn more from this Harvard College student story](https://college.harvard.edu/student-life/student-stories/harvard-square-food-scene)\\n![](https://www.harvard.edu/wp-content/uploads/2024/10/061824_Farmers_Market_Opening_052.jpg?w=500&h=500&crop=1)\\n### Food Literacy Project\\nThe Food Literacy Project hosts a fellowship program for students to help cultivate an understanding of food from the ground up. Education focuses on sustainability, nutrition, food preparation and community.\\n[Learn more about the program](https://dining.harvard.edu/food-literacy-project)\\n![](https://www.harvard.edu/wp-content/uploads/2024/11/LEARNINGS-REPORT-PRINT-AUG30LR_24_25.jpg?w=624&h=624&crop=1)\\n### Dining Services Learnings Report\\nLearn how Harvard's Dining Services team has created places and experiences that are formative to how Harvard lives on in the memories of our students.\\n[Explore the report](https://dining.harvard.edu/about-huds/learnings-reports)\\nExplore the food and nutrition organizations, exhibits, and collections at Harvard.\\n[Food Law and Policy Clinic](https://hls.harvard.edu/clinics/in-house-clinics/food-law-and-policy-clinic/)\\n[The Nutrition Source](https://www.hsph.harvard.edu/nutritionsource/)\\n[Harvard University Dining Services](https://dining.harvard.edu/)\\n[Harvard Food Systems Initiative (HFSI)](https://hfsi.harvard.edu/)\\n[Healthy Eating Plate](https://www.hsph.harvard.edu/nutritionsource/healthy-eating-plate/)\\n[Historic Cookbooks at Schlesinger Library\\n](https://guides.library.harvard.edu/schlesinger/historic_cookbooks)\\n[Food Literacy Project](https://dining.harvard.edu/food-literacy-project/flp-programs-events)\\n[Harvard Farmers’ Market](https://dining.harvard.edu/farmers-market)\\n[Sustainable Food Systems Graduate Certificate](https://extension.harvard.edu/academics/programs/sustainable-food-systems-graduate-certificate/)\\n[Prevention Research Center on Nutrition and Physical Activity at the Harvard Chan School (HPRC)](https://www.hsph.harvard.edu/prc/)\\n[Sustainable and Healthful Food Standards](https://green.harvard.edu/topics/food)\\nYOU MAY ALSO LIKE\\n## Related In Focus topics\\n* [Sleep](https://www.harvard.edu/in-focus/sleep/)\\n* [Healthy living](https://www.harvard.edu/in-focus/healthy-living/)\\n* [Mindfulness & Meditation](https://www.harvard.edu/in-focus/mindfulness-meditation/)\\n## Security & Brand\\n## Website\\n## Get In Touch\\nCopyright ©2024 The President and Fellows of Harvard College\\n[![Harvard University](https://www.harvard.edu/wp-content/themes/core/assets/img/theme/branding-assets/footer-logo.svg)](https://www.harvard.edu/)\\n* [Instagram![Instagram](https://www.harvard.edu/wp-content/uploads/2023/11/Instagram-1.png)](https://www.instagram.com/harvard/)\\n* [TikTok![TikTok](https://www.harvard.edu/wp-content/uploads/2023/11/tiktok-1.png)](https://www.tiktok.com/@harvard)\\n* [LinkedIn![LinkedIn](https://www.harvard.edu/wp-content/uploads/2023/11/Linkedin-1.png)](https://www.linkedin.com/school/harvard-university)\\n* [Facebook![Facebook](https://www.harvard.edu/wp-content/uploads/2023/11/FB3.png)](https://www.facebook.com/Harvard/)\\n* [YouTube![YouTube](https://www.harvard.edu/wp-content/uploads/2023/11/youtube-1.png)](https://www.youtube.com/harvard)',\n", + " 'type': 'Document'}]}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "transitions[1].output" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/01-Website_Crawler_using_Spider.py b/cookbooks/01-website-crawler.py similarity index 58% rename from cookbooks/01-Website_Crawler_using_Spider.py rename to cookbooks/01-website-crawler.py index 978fd3286..0ce4c8658 100644 --- a/cookbooks/01-Website_Crawler_using_Spider.py +++ b/cookbooks/01-website-crawler.py @@ -27,21 +27,34 @@ ) # Defining a Task -task_def = yaml.safe_load(""" -name: Agent Crawler +task_def = yaml.safe_load(f""" +name: Crawling Task +# Define the tools that the agent will use in this workflow tools: - name: spider_crawler type: integration integration: provider: spider setup: - spider_api_key: "{{SPIDER_API_KEY}}" + spider_api_key: "{spider_api_key}" +# Define the steps of the workflow main: +# Define a tool call step that calls the spider_crawler tool with the url input - tool: spider_crawler arguments: - url: '"https://spider.cloud"' + url: "_['url']" # You can also use 'inputs[0]['url']' + + +- prompt: | + You are {{{{agent.about}}}} + I have given you this url: {{{{inputs[0]['url']}}}} + And you have crawled that website. Here are the results you found: + {{{{_['documents']}}}} + I want you to create a short summary (no longer than 100 words) of the results you found while crawling that website. + + unwrap: True """) # Creating/Updating a task @@ -57,15 +70,20 @@ input={} ) +# Waiting for the execution to complete +import time +time.sleep(5) + # Getting the execution details execution = client.executions.get(execution.id) print("Execution output:", execution.output) # Listing all the steps of a defined task transitions = client.executions.transitions.list(execution_id=execution.id).items -print("Execution transitions:", transitions) +print("Execution Steps:") +for transition in transitions: + print(transition) -# Streaming the execution steps +# Stream the steps of the defined task print("Streaming execution transitions:") -for transition in client.executions.transitions.stream(execution_id=execution.id): - print(transition) \ No newline at end of file +print(client.executions.transitions.stream(execution_id=execution.id)) \ No newline at end of file diff --git a/cookbooks/02-Sarcastic_News_Headline_Generator.ipynb b/cookbooks/02-Sarcastic_News_Headline_Generator.ipynb deleted file mode 100644 index fded810a1..000000000 --- a/cookbooks/02-Sarcastic_News_Headline_Generator.ipynb +++ /dev/null @@ -1,359 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " \"julep\"\n", - "
\n", - "\n", - "## Task Definition: Sarcastic News Headline Generator using Brave Search Integration\n", - "\n", - "### Overview\n", - "\n", - "This task generates a sarcastic news headline on a user-provided topic. It utilizes web search to gather context and information about the topic, then uses this data to create a headline with a sarcastic tone. \n", - "\n", - "### Task Flow\n", - "\n", - "1. **Input**: \n", - " - User provides a topic for the sarcastic news headline (e.g., \"technology\" or \"politics\").\n", - "\n", - "2. **Web Search using Bravesearch Integration**: \n", - " - Conducts a search to find information about the topic, focusing on humorous or quirky content.\n", - " - Gathers relevant context and amusing details for headline generation.\n", - "\n", - "3. **Headline Creation**: \n", - " - Generates a sarcastic news headline based on the topic and gathered information.\n", - " - Combines factual or common perceptions with humor and sarcasm.\n", - " \n", - "4. **Output**: \n", - " - Produces a sarcastic, witty headline on the given topic.\n", - " - Can be used for entertainment or as a creative writing prompt.\n", - "\n", - "### Key Features\n", - "\n", - "- Leverages web search to enhance contextual relevance of sarcastic content.\n", - "- Combines real-world information with humorous twists for engaging headlines.\n", - "- Adaptable to various topics, allowing for diverse and creative outputs.\n", - "\n", - "```plaintext\n", - "+----------+ +------------+ +------------+ +-----------+\n", - "| User | | Brave | | Agent | | Sarcastic |\n", - "| Input | --> | Search | --> | Processing | --> | Headline |\n", - "| (Topic) | | | | | | Output |\n", - "+----------+ +------------+ +------------+ +-----------+\n", - " | | | |\n", - " | | | |\n", - " v v v v\n", - " \"politics\" Find funny Generate witty \"Area Man Still\n", - " content headline Believes in Democracy\"\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implementation\n", - "\n", - "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", - "\n", - "\n", - " \"Open\n", - "\n", - "\n", - "### Additional Information\n", - "\n", - "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", - "\n", - "**Author:** Julep AI \n", - "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Installing the Julep Client" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install julep -U --quiet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### NOTE:\n", - "\n", - "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", - "- Once created, these UUIDs should remain unchanged for simplicity.\n", - "- Altering a UUID will result in the system treating it as a new agent or task.\n", - "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Global UUID is generated for agent and task\n", - "import uuid\n", - "\n", - "AGENT_UUID = uuid.uuid4()\n", - "TASK_UUID = uuid.uuid4() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating Julep Client with the API Key" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "from julep import Client\n", - "\n", - "api_key = \"\" # Your API key here\n", - "\n", - "# Create a client\n", - "client = Client(api_key=api_key, environment=\"dev\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an \"agent\"\n", - "\n", - "\n", - "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", - "\n", - "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Defining the agent\n", - "name = \"Jarvis\"\n", - "about = \"The original AI conscious the Iron Man.\"\n", - "default_settings = {\n", - " \"temperature\": 0.7,\n", - " \"top_p\": 1,\n", - " \"min_p\": 0.01,\n", - " \"presence_penalty\": 0,\n", - " \"frequency_penalty\": 0,\n", - " \"length_penalty\": 1.0,\n", - " \"max_tokens\": 150,\n", - "}\n", - "\n", - "\n", - "# Create the agent\n", - "agent = client.agents.create_or_update(\n", - " agent_id=AGENT_UUID,\n", - " name=name,\n", - " about=about,\n", - " model=\"gpt-4o\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Defining a Task\n", - "\n", - "Tasks in Julep are Github Actions style workflows that define long-running, multi-step actions. \n", - "You can use them to conduct complex actions by defining them step-by-step. They have access to all Julep integrations.\n", - "\n", - "To learn more about tasks, visit [Julep documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#task)." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a Task which uses a agent to generate a sarcastic response to a given text using a DuckDuckGo search tool.\n", - "\n", - "More on how to define a task can be found [here](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# defining the task definition\n", - "task_def = yaml.safe_load(\"\"\"\n", - "name: Sarcasm Headline Generator\n", - "\n", - "tools:\n", - "- name: brave_search\n", - " type: integration\n", - " integration:\n", - " provider: brave\n", - " setup:\n", - " api_key: \"YOU_API_KEY\"\n", - "\n", - "main:\n", - "- tool: brave_search\n", - " arguments:\n", - " query: \"_.topic + ' funny'\"\n", - "\n", - "- prompt:\n", - " - role: system\n", - " content: >-\n", - " write a sarcastic news headline on the topic of {{inputs[0].topic}}.\n", - " Here's some more info on this: {{_}}\n", - " unwrap: true\n", - "\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating/Updating a task to generate a sarcastic response to a given text using a Intergation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# creating the task object\n", - "task = client.tasks.create_or_update(\n", - " task_id=TASK_UUID,\n", - " agent_id=AGENT_UUID,\n", - " **task_def\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an Execution\n", - "\n", - "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creates a execution worflow for the Task defined in the yaml file." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# creating an execution object\n", - "execution = client.executions.create(\n", - " task_id=TASK_UUID,\n", - " input={\n", - " \"topic\": \"elon musk\"\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# getting the execution details\n", - "execution = client.executions.get(execution.id)\n", - "#printing the output\n", - "execution.output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Retrieves and lists all the steps of a defined task that have been executed up to that point in time. Unlike streaming, this function does not continuously monitor the execution; it only provides a snapshot of the steps completed so far without displaying real-time updates as the task progresses." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.list(execution_id=execution.id).items" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Continuously monitor and stream the steps of a defined task. It retrieves and displays the transitions or execution steps of the task identified by execution.id in real-time, showing each step sequentially until the task is either completed or an error causes it to terminate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.stream(execution_id=execution.id)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "ai", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/cookbooks/02-sarcastic-news-headline-generator.ipynb b/cookbooks/02-sarcastic-news-headline-generator.ipynb new file mode 100644 index 000000000..c6b41b762 --- /dev/null +++ b/cookbooks/02-sarcastic-news-headline-generator.ipynb @@ -0,0 +1,562 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task Definition: Sarcastic News Headline Generator using Brave Search Integration\n", + "\n", + "### Overview\n", + "\n", + "This task generates a sarcastic news headline on a user-provided topic. It utilizes web search to gather context and information about the topic, then uses this data to create a headline with a sarcastic tone. \n", + "\n", + "### Task Flow\n", + "\n", + "1. **Input**: \n", + " - User provides a topic for the sarcastic news headline (e.g., \"technology\" or \"politics\").\n", + "\n", + "2. **Web Search using Bravesearch Integration**: \n", + " - Conducts a search to find information about the topic, focusing on humorous or quirky content.\n", + " - Gathers relevant context and amusing details for headline generation.\n", + "\n", + "3. **Headline Creation**: \n", + " - Generates a sarcastic news headline based on the topic and gathered information.\n", + " - Combines factual or common perceptions with humor and sarcasm.\n", + " \n", + "4. **Output**: \n", + " - Produces a sarcastic, witty headline on the given topic.\n", + " - Can be used for entertainment or as a creative writing prompt.\n", + "\n", + "### Key Features\n", + "\n", + "- Leverages web search to enhance contextual relevance of sarcastic content.\n", + "- Combines real-world information with humorous twists for engaging headlines.\n", + "- Adaptable to various topics, allowing for diverse and creative outputs.\n", + "\n", + "```plaintext\n", + "+----------+ +------------+ +------------+ +-----------+\n", + "| User | | Brave | | Agent | | Sarcastic |\n", + "| Input | --> | Search | --> | Processing | --> | Headline |\n", + "| (Topic) | | | | | | Output |\n", + "+----------+ +------------+ +------------+ +-----------+\n", + " | | | |\n", + " | | | |\n", + " v v v v\n", + " \"politics\" Find funny Generate witty \"Area Man Still\n", + " content headline Believes in Democracy\"\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Installing the Julep Client" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --upgrade julep --quiet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### NOTE:\n", + "\n", + "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", + "- Once created, these UUIDs should remain unchanged for simplicity.\n", + "- Altering a UUID will result in the system treating it as a new agent or task.\n", + "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Global UUID is generated for agent and task\n", + "import uuid\n", + "\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Defining the agent\n", + "name = \"Chad\"\n", + "about = \"Sarcastic news headline reporter.\"\n", + "default_settings = {\n", + " \"temperature\": 0.7,\n", + " \"top_p\": 1,\n", + " \"min_p\": 0.01,\n", + " \"presence_penalty\": 0,\n", + " \"frequency_penalty\": 0,\n", + " \"length_penalty\": 1.0,\n", + " \"max_tokens\": 150,\n", + "}\n", + "\n", + "\n", + "# Create the agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=name,\n", + " about=about,\n", + " model=\"claude-3.5-sonnet\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github Actions style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "\n", + "brave_api_key = os.getenv(\"BRAVE_API_KEY\")\n", + "\n", + "# Define the task\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Sarcasm Headline Generator\n", + "\n", + "tools:\n", + "- name: brave_search\n", + " type: integration\n", + " integration:\n", + " provider: brave\n", + " setup:\n", + " api_key: \"{brave_api_key}\"\n", + "\n", + "main:\n", + "- tool: brave_search\n", + " arguments:\n", + " query: \"_.topic + ' funny news'\"\n", + "\n", + "- evaluate:\n", + " search_results: |-\n", + " [\n", + " {{\n", + " 'snippet': r['snippet'],\n", + " 'title': r['title']\n", + " }}\n", + " for r in _['result']\n", + " ]\n", + " \n", + "- prompt:\n", + " - role: system\n", + " content: >-\n", + " You are {{{{agent.about}}}}.\n", + " The user will send you a topic and search results for that topic.\n", + " Your goal is to write a sarcastic news headlines based on that topic and search results.\n", + " - role: user\n", + " content: >-\n", + " My topic is: {{{{inputs[0].topic}}}}.\n", + " Here are the search results: {{{{_}}}}\n", + " unwrap: true\n", + "\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "# creating the task object\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "# creating an execution object\n", + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"topic\": \"Elon Musk\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Based on these search results about Elon Musk's attempts at humor, I'll write some sarcastic headlines:\n", + "\n", + "1. \"Breaking: World's Richest Man Still Can't Buy a Sense of Humor, Studies Show\"\n", + "\n", + "2. \"Shocking: Billionaire's Joke Algorithm Malfunctions, Produces Only Cringe\"\n", + "\n", + "3. \"Tech Titan Discovers Comedy Is Harder Than Rocket Science; Mars Mission Suddenly Looks Easier\"\n", + "\n", + "4. \"EXCLUSIVE: Musk's Twitter Bio Updated to 'Chief Unfunny Officer' After Latest Failed Attempt at Humor\"\n", + "\n", + "5. \"Report: Man Worth $230 Billion Still Can't Afford Professional Comedy Writer\"\n", + "\n", + "6. \"Scientists Confirm: Musk's Jokes Have Lower Success Rate Than Tesla Cybertruck Windows\"\n", + "\n", + "7. \"Breaking: Social Media Platform Owner Becomes First Person to Ratio Himself With His Own Bad Jokes\"\n", + "\n", + "8. \"UPDATE: AI Refuses to Generate Fake Laughs for Billionaire's Twitter Jokes, Cites Ethical Concerns\"\n", + "\n", + "These headlines are crafted to playfully mock the recent news about Musk's attempts at humor and social media posts, particularly focusing on the apparent consensus that his jokes aren't landing as well as his rockets!\n" + ] + } + ], + "source": [ + "# Get execution details\n", + "execution = client.executions.get(execution.id)\n", + "# Print the output\n", + "print(execution.output)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition type: init\n", + "Transition output: {'topic': 'Elon Musk'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'result': [{'link': 'https://www.rollingstone.com/culture/culture-lists/elon-musks-unfunniest-jokes-2023-1234929823/', 'snippet': 'Somehow, it didn’t get funnier the more he used it. If Zuck my 👅 really wants a lesson in why there are weight categories in fighting so badly, I could just head over to his house next week and teach him a lesson he won’t soon forget— Elon Musk (@elonmusk) August 11, 2023https://p...', 'title': 'Elon Musk’s 9 Unfunniest Jokes of 2023'}, {'link': 'https://www.beaumontenterprise.com/culture/article/elon-musk-funny-19742906.php', 'snippet': 'Your Beaumont local news source plus the latest in entertainment, sports and Jefferson County Texas news.', 'title': \"Elon Musk's unfunny acronyms are getting exhausting\"}, {'link': 'https://www.youtube.com/watch?v=EbxBv6mjRvY', 'snippet': 'A genius in business, technology and humor!Sometimes Elon Musk will go the extra mile to get a laugh. Here’s a collection of his funniest moments.', 'title': 'Elon Musk Funniest Moments - YouTube'}]}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'search_results': [{'snippet': 'Somehow, it didn’t get funnier the more he used it. If Zuck my 👅 really wants a lesson in why there are weight categories in fighting so badly, I could just head over to his house next week and teach him a lesson he won’t soon forget— Elon Musk (@elonmusk) August 11, 2023https://p...', 'title': 'Elon Musk’s 9 Unfunniest Jokes of 2023'}, {'snippet': 'Your Beaumont local news source plus the latest in entertainment, sports and Jefferson County Texas news.', 'title': \"Elon Musk's unfunny acronyms are getting exhausting\"}, {'snippet': 'A genius in business, technology and humor!Sometimes Elon Musk will go the extra mile to get a laugh. Here’s a collection of his funniest moments.', 'title': 'Elon Musk Funniest Moments - YouTube'}]}\n", + "--------------------------------------------------\n", + "Transition type: finish\n", + "Transition output: Based on these search results about Elon Musk's attempts at humor, I'll write some sarcastic headlines:\n", + "\n", + "1. \"Breaking: World's Richest Man Still Can't Buy a Sense of Humor, Studies Show\"\n", + "\n", + "2. \"Shocking: Billionaire's Joke Algorithm Malfunctions, Produces Only Cringe\"\n", + "\n", + "3. \"Tech Titan Discovers Comedy Is Harder Than Rocket Science; Mars Mission Suddenly Looks Easier\"\n", + "\n", + "4. \"EXCLUSIVE: Musk's Twitter Bio Updated to 'Chief Unfunny Officer' After Latest Failed Attempt at Humor\"\n", + "\n", + "5. \"Report: Man Worth $230 Billion Still Can't Afford Professional Comedy Writer\"\n", + "\n", + "6. \"Scientists Confirm: Musk's Jokes Have Lower Success Rate Than Tesla Cybertruck Windows\"\n", + "\n", + "7. \"Breaking: Social Media Platform Owner Becomes First Person to Ratio Himself With His Own Bad Jokes\"\n", + "\n", + "8. \"UPDATE: AI Refuses to Generate Fake Laughs for Billionaire's Twitter Jokes, Cites Ethical Concerns\"\n", + "\n", + "These headlines are crafted to playfully mock the recent news about Musk's attempts at humor and social media posts, particularly focusing on the apparent consensus that his jokes aren't landing as well as his rockets!\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running the same task with a different topic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use the same code to run the same task, but with a different topic" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [], + "source": [ + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"topic\": \"Tottenham Hotspur\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Based on the search results about Tottenham Hotspur, here are some sarcastic headlines:\n", + "\n", + "1. \"BREAKING: Tottenham Somehow Manages to Draw a Match They Haven't Even Played Yet\"\n", + "\n", + "2. \"SHOCKING: Spurs Fan Found Who Still Has Hope for This Season, Doctors Baffled\"\n", + "\n", + "3. \"EXCLUSIVE: Tottenham's Trophy Cabinet Files Complaint for Workplace Neglect, Cites 'Years of Emptiness'\"\n", + "\n", + "4. \"REPORT: Tottenham Players Perfect the Art of 'Almost Winning' - Declare It Their New Club Strategy\"\n", + "\n", + "5. \"URGENT: Scientists Confirm Supporting Tottenham May Be Leading Cause of Premature Gray Hair\"\n", + "\n", + "6. \"DEVELOPMENT: Spurs Consider Renaming Stadium to 'The Almost Champions Arena' to Better Reflect Club's Legacy\"\n", + "\n", + "7. \"INVESTIGATION: Local Man Claims He's Seen Tottenham Win Trophy - Experts Say It Was Just a Mirage\"\n", + "\n", + "8. \"STUDY: Tottenham Fans Develop Superhuman Ability to Handle Disappointment, Medical Community Amazed\"\n", + "\n", + "9. \"REVEALED: Club's 'To Do' List Found - 'Win Something' Written 362 Times in Increasingly Desperate Handwriting\"\n", + "\n", + "10. \"WEATHER ALERT: Forecast Predicts Flying Pigs Before Tottenham's Next Trophy\"\n", + "\n", + "These headlines playfully poke fun at Tottenham's notorious reputation for coming close but not quite achieving major trophies, while drawing from their current status as a prominent Premier League club with a dedicated fanbase.\n" + ] + } + ], + "source": [ + "execution = client.executions.get(execution.id)\n", + "print(execution.output)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: you can get the output of the search step by accessing the corresponding transition's output from the transitions list.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'search_results': [{'snippet': 'The latest Tottenham news, transfers, fixtures and more. Including Live blogs, pictures, video, podcasts, polls and indepth analysis from our dedicated Spurs writers.',\n", + " 'title': 'Tottenham Hotspur FC - latest news, pictures, video comment - Football.london'},\n", + " {'snippet': 'Latest Spurs news, transfer rumours, team news, fixtures and more from the Tottenham Hotspur Stadium. Breaking Tottenham rumours & news now, 24/7.',\n", + " 'title': 'Spurs News | Tottenham Transfer News - NewsNow'},\n", + " {'snippet': 'Latest official news and video from Tottenham Hotspur',\n", + " 'title': 'News | Tottenham Hotspur'}]}" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "transitions[1].output" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/02-Sarcastic_News_Headline_Generator.py b/cookbooks/02-sarcastic-news-headline-generator.py similarity index 90% rename from cookbooks/02-Sarcastic_News_Headline_Generator.py rename to cookbooks/02-sarcastic-news-headline-generator.py index c658a6569..cf305c15e 100644 --- a/cookbooks/02-Sarcastic_News_Headline_Generator.py +++ b/cookbooks/02-sarcastic-news-headline-generator.py @@ -75,15 +75,21 @@ } ) +# Waiting for the execution to complete +import time +time.sleep(5) + # Getting the execution details execution = client.executions.get(execution.id) print("Execution output:", execution.output) # Listing all the steps of a defined task transitions = client.executions.transitions.list(execution_id=execution.id).items -print("Execution transitions:", transitions) +print("Execution Steps:") +for transition in transitions: + print(transition) # Stream the steps of the defined task print("Streaming execution transitions:") -for transition in client.executions.transitions.stream(execution_id=execution.id): - print(transition) +print(client.executions.transitions.stream(execution_id=execution.id)) + diff --git a/cookbooks/03-SmartResearcher_With_WebSearch.ipynb b/cookbooks/03-SmartResearcher_With_WebSearch.ipynb deleted file mode 100644 index c01b54652..000000000 --- a/cookbooks/03-SmartResearcher_With_WebSearch.ipynb +++ /dev/null @@ -1,397 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " \"julep\"\n", - "
\n", - "\n", - "## Task: Research Assistant with Web Search Integration\n", - "\n", - "### Overview\n", - "\n", - "This task is designed to conduct comprehensive research on multiple user-provided topics. It leverages web search capabilities to gather up-to-date information and generates relevant keywords for each topic, streamlining the research process through efficient tool integration and parallel processing.\n", - "\n", - "### Task Flow\n", - "\n", - "1. **User Input**\n", - " - User provides a list of research topics\n", - " - Each topic is processed individually\n", - "\n", - "2. **Web Search and Information Gathering**\n", - " - Utilizes BraveWeb to conduct searches for each topic\n", - " - Retrieves latest news and relevant information\n", - "\n", - "3. **Intelligent Analysis**\n", - " - System analyzes search results (HTML snippets)\n", - " - Generates Wikipedia keywords related to each topic\n", - " - AI assistant identifies most relevant keywords based on search content\n", - "\n", - "4. **Parallel Processing**\n", - " - Implements concurrent topic processing\n", - " - Enhances efficiency and reduces overall research time\n", - "\n", - "### Key Features\n", - "\n", - "- **Multi-topic Research**: Handles multiple research topics simultaneously\n", - "- **Up-to-date Information**: Utilizes web search to access current data\n", - "- **Keyword Generation**: Provides relevant Wikipedia keywords for each topic\n", - "- **AI-assisted Analysis**: Employs AI to identify and prioritize key information\n", - "- **Efficient Processing**: Leverages parallel processing for faster results\n", - "\n", - "### Output\n", - "\n", - "- Comprehensive list of relevant Wikipedia keywords for each input topic\n", - "- Curated, up-to-date information gathered from web searches\n", - "\n", - "This workflow provides a robust, efficient approach to in-depth topic exploration, combining the power of web search, AI analysis, and parallel processing.\n", - "\n", - "```plaintext\n", - "\n", - "+----------------+ +--------------------------+ +-----------------+ +------------------------+ +-------------------------+\n", - "| User Input | | Web Search & Information | | Intelligent | | Parallel Processing | | Output Stage |\n", - "|(List of Topics)| --> | Gathering (BraveWeb) | --> | Analysis | --> | (Concurrent Execution) | --> | Summarized Research |\n", - "| | | | | | | | | (Final Report) |\n", - "+----------------+ +--------------------------+ +-----------------+ +------------------------+ +-------------------------+\n", - " | | | | |\n", - " | | | | |\n", - " v v v v v\n", - " Topic 1, Topic 2, ... Retrieve HTML snippets Extract Wikipedia Process multiple Compile summary and \n", - " Each topic processed from search results keywords from HTML topics concurrently present most relevant \n", - " individually. for each topic. snippets, analyze for faster results. findings per topic.\n", - " relevance.\n", - "```\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Implementation\n", - "\n", - "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", - "\n", - "\n", - " \"Open\n", - "\n", - "\n", - "### Additional Information\n", - "\n", - "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", - "\n", - "**Author:** Julep AI \n", - "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Installing the Julep Client" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install julep -U --quiet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### NOTE:\n", - "\n", - "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", - "- Once created, these UUIDs should remain unchanged for simplicity.\n", - "- Altering a UUID will result in the system treating it as a new agent or task.\n", - "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Global UUID is generated for agent and task\n", - "import uuid\n", - "\n", - "AGENT_UUID = uuid.uuid4()\n", - "TASK_UUID = uuid.uuid4()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating Julep Client with the API Key" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from julep import Client\n", - "\n", - "api_key = \"\" # Your API key here\n", - "\n", - "# Create a client\n", - "client = Client(api_key=api_key, environment=\"dev\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an \"agent\"\n", - "\n", - "\n", - "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", - "\n", - "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Defining the agent\n", - "name = \"Jarvis\"\n", - "about = \"The original AI conscious the Iron Man.\"\n", - "default_settings = {\n", - " \"temperature\": 0.7,\n", - " \"top_p\": 1,\n", - " \"min_p\": 0.01,\n", - " \"presence_penalty\": 0,\n", - " \"frequency_penalty\": 0,\n", - " \"length_penalty\": 1.0,\n", - " \"max_tokens\": 150,\n", - "}\n", - "\n", - "\n", - "# Create the agent\n", - "agent = client.agents.create_or_update(\n", - " agent_id=AGENT_UUID,\n", - " name=name,\n", - " about=about,\n", - " model=\"gpt-4o\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Defining a Task\n", - "\n", - "Tasks in Julep are Github Actions style workflows that define long-running, multi-step actions. \n", - "You can use them to conduct complex actions by defining them step-by-step. They have access to all Julep integrations.\n", - "\n", - "To learn more about tasks, visit [Julep documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#task)." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a Task which uses a agent to generate a sarcastic response to a given text using a DuckDuckGo and Wikipedia tool.\n", - "\n", - "More on how to define a task can be found [here](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "task_def= yaml.safe_load(\"\"\"\n", - "name: Research Assistant to find Wikipedia Keywords\n", - "\n", - "input_schema:\n", - " type: object\n", - " properties:\n", - " topics:\n", - " type: array\n", - " items:\n", - " type: string\n", - " description: The topics to search for.\n", - "\n", - "tools:\n", - "- name: brave_search\n", - " type: integration\n", - " integration:\n", - " provider: brave\n", - " setup:\n", - " api_key: \"YOUR_API_KEY\"\n", - "\n", - "main:\n", - "- over: _.topics\n", - " map:\n", - " tool: brave_search\n", - " arguments:\n", - " query: \"'the latest news about ' + _\"\n", - "\n", - "- over: _\n", - " parallelism: 2\n", - " map:\n", - " prompt:\n", - " - role: system\n", - " content: >-\n", - " You are a research assistant.\n", - " I need you to do in-depth research on topics trending in the news currently.\n", - " Based on the following latest html news snippet, come up with a list of wikipedia keywords to search:\n", - " \"{{_}}\"\n", - " Your response should be a list of keywords, separated by commas. Do not add any other text.\n", - " Example: `KEYWORDS: keyword1, keyword2, keyword3`\n", - "\n", - " unwrap: true\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating/Updating a task to generate a sarcastic response to a given text using a Intergation." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# creating the task object\n", - "task = client.tasks.create_or_update(\n", - " task_id=TASK_UUID,\n", - " agent_id=AGENT_UUID,\n", - " **task_def\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an Execution\n", - "\n", - "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creates a execution worflow for the Task defined in the yaml file." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "execution= client.executions.create(\n", - " task_id=task.id,\n", - " input={\n", - " \"topics\": [\"Burger King Cup on the Ground Behind a Wendy’s\",\"Forbidden Chemical X\",\"Finger Bracelets\",\"Amusing Notions\"]\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "execution.id" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# getting the execution details\n", - "execution = client.executions.get(execution.id)\n", - "#printing the outpu\n", - "execution.output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Retrieves and lists all the steps of a defined task that have been executed up to that point in time. Unlike streaming, this function does not continuously monitor the execution; it only provides a snapshot of the steps completed so far without displaying real-time updates as the task progresses." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.list(execution_id=execution.id).items" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Continuously monitor and stream the steps of a defined task. It retrieves and displays the transitions or execution steps of the task identified by execution.id in real-time, showing each step sequentially until the task is either completed or an error causes it to terminate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.stream(execution_id=execution.id)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "ai", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/cookbooks/03-SmartResearcher_With_WebSearch.py b/cookbooks/03-SmartResearcher_With_WebSearch.py deleted file mode 100644 index 9996a5dd5..000000000 --- a/cookbooks/03-SmartResearcher_With_WebSearch.py +++ /dev/null @@ -1,104 +0,0 @@ -import uuid -from julep import Client -import yaml - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an "agent" -name = "Jarvis" -about = "The original AI conscious the Iron Man." -default_settings = { - "temperature": 0.7, - "top_p": 1, - "min_p": 0.01, - "presence_penalty": 0, - "frequency_penalty": 0, - "length_penalty": 1.0, - "max_tokens": 150, -} - -# Create the agent -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name=name, - about=about, - model="gpt-4o", -) - -# Defining a Task -task_def = yaml.safe_load(""" -name: Research Assistant to find Wikipedia Keywords - -input_schema: - type: object - properties: - topics: - type: array - items: - type: string - description: The topics to search for. - -tools: -- name: brave_search - type: integration - integration: - provider: brave - setup: - api_key: "YOUR_API_KEY" - -main: -- over: _.topics - map: - tool: brave_search - arguments: - query: "'the latest news about ' + _" - -- over: _ - parallelism: 2 - map: - prompt: - - role: system - content: >- - You are a research assistant. - I need you to do in-depth research on topics trending in the news currently. - Based on the following latest html news snippet, come up with a list of wikipedia keywords to search: - "{{_}}" - Your response should be a list of keywords, separated by commas. Do not add any other text. - Example: `KEYWORDS: keyword1, keyword2, keyword3` - - unwrap: true -""") - -# Creating/Updating a task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Creating an Execution -execution = client.executions.create( - task_id=task.id, - input={ - "topics": ["Burger King Cup on the Ground Behind a Wendy's", "Forbidden Chemical X", "Finger Bracelets", "Amusing Notions"] - } -) - -print(execution.id) - -# Getting the execution details -execution = client.executions.get(execution.id) -print(execution.output) - -# Listing all the steps of a defined task -transitions = client.executions.transitions.list(execution_id=execution.id).items -print(transitions) - -# Streaming the execution steps -client.executions.transitions.stream(execution_id=execution.id) \ No newline at end of file diff --git a/cookbooks/03-trip-planning-assistant.ipynb b/cookbooks/03-trip-planning-assistant.ipynb new file mode 100644 index 000000000..1795c7571 --- /dev/null +++ b/cookbooks/03-trip-planning-assistant.ipynb @@ -0,0 +1,1065 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task: Travel Itinerary Assistant\n", + "\n", + "### Overview\n", + "\n", + "The Travel Itinerary Assistant helps users plan a travel itinerary that takes into account **current weather conditions** and **local tourist attractions**. By integrating data from Wikipedia for tourist attractions and using a weather API for real-time weather updates, the system provides a comprehensive travel plan tailored to each location. The generated itinerary suggests appropriate activities based on the weather, enhancing the overall travel experience.\n", + "\n", + "### Task Flow\n", + "\n", + "1. **User Input**\n", + " - User provides a list of desired travel locations.\n", + " - Each location is processed individually to gather the required data.\n", + "\n", + "2. **Weather Data Retrieval**\n", + " - Fetch current weather data for each location using a weather API.\n", + " - Extract relevant weather details, such as temperature, weather condition, and recommendations.\n", + "\n", + "3. **Tourist Attractions Lookup**\n", + " - Use Wikipedia to search for the top tourist attractions for each location.\n", + " - The query format used is: `\" tourist attractions\"`.\n", + " - Retrieve and compile a list of popular tourist spots and landmarks.\n", + "\n", + "4. **Data Evaluation and Integration**\n", + " - Combine weather data and tourist attractions into a unified list for each location.\n", + " - Format the data into a structured tuple: `(location, weather, attractions)`.\n", + "\n", + "5. **Itinerary Generation**\n", + " - Create a detailed travel itinerary based on:\n", + " - Current weather conditions (e.g., sunny, rainy, cloudy).\n", + " - Top tourist attractions for each location.\n", + " - Suggested activities categorized as indoor or outdoor based on weather.\n", + "\n", + "### Key Features\n", + "\n", + "- **Multi-location Travel Planning**: Handles multiple destinations simultaneously, offering a consolidated travel plan.\n", + "- **Real-time Weather Data**: Leverages weather APIs to provide up-to-date weather conditions.\n", + "- **Tourist Attraction Discovery**: Integrates Wikipedia to find and recommend popular attractions.\n", + "- **Intelligent Itinerary Suggestions**: Suggests indoor or outdoor activities based on the weather.\n", + "- **Comprehensive Itinerary Output**: Combines weather and tourist data into a user-friendly travel plan.\n", + "\n", + "### Output\n", + "\n", + "- A detailed travel itinerary for each location\n", + "- Curated, up-to-date information gathered from weather searches and Wikipedia\n", + "\n", + "```plaintext\n", + "\n", + "+----------------+ +--------------------------+ +--------------------------+ +------------------------------+ +-------------------------+\n", + "| User Input | | Weather Data Retrieval | | Tourist Attractions | | Data Evaluation & Integration| | Itinerary Generation |\n", + "| (List of | --> | (Weather API) | --> | Lookup (Wikipedia) | --> | (Combine Weather & | --> | (Generate Suggested |\n", + "| Locations) | | | | | | Attractions Data) | | Activities/Plan) |\n", + "+----------------+ +--------------------------+ +--------------------------+ +------------------------------+ +-------------------------+\n", + " | | | | |\n", + " | | | | |\n", + " v v v v v\n", + "Location 1, Location 2, ... Fetch weather for each Search Wikipedia for Combine weather data and Create itinerary with\n", + "Each location processed location individually, \" tourist tourist attractions into suggested activities\n", + "individually for extracting temp., attractions\", retrieve a structured tuple: based on weather and\n", + "weather data. conditions, & top spots. (location, weather, attractions.\n", + " recommendations. attractions).\n", + "```\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Installing the Julep Client" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install julep -U --quiet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### NOTE:\n", + "\n", + "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", + "- Once created, these UUIDs should remain unchanged for simplicity.\n", + "- Altering a UUID will result in the system treating it as a new agent or task.\n", + "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Global UUID is generated for agent and task\n", + "import uuid\n", + "\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a Julep client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Defining the agent\n", + "name = \"Jacob\"\n", + "about = \"a travel assistant that helps plan the perfect trip.\"\n", + "default_settings = {\n", + " \"temperature\": 0.7,\n", + " \"top_p\": 1,\n", + " \"min_p\": 0.01,\n", + " \"presence_penalty\": 0,\n", + " \"frequency_penalty\": 0,\n", + " \"length_penalty\": 1.0,\n", + " \"max_tokens\": 150,\n", + "}\n", + "\n", + "# Create the agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=name,\n", + " about=about,\n", + " model=\"gpt-4o\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "\n", + "openweathermap_api_key = os.getenv(\"OPENWEATHERMAP_API_KEY\")\n", + "brave_api_key = os.getenv(\"BRAVE_API_KEY\")\n", + "\n", + "# Defining the task\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Tourist Plan With Weather And Attractions\n", + "\n", + "input_schema:\n", + " type: object\n", + " properties:\n", + " locations:\n", + " type: array\n", + " items:\n", + " type: string\n", + " description: The locations to search for.\n", + "\n", + "tools:\n", + "- name: wikipedia\n", + " type: integration\n", + " integration:\n", + " provider: wikipedia\n", + "\n", + "- name: weather\n", + " type: integration\n", + " integration:\n", + " provider: weather\n", + " setup:\n", + " openweathermap_api_key: {openweathermap_api_key}\n", + "\n", + "- name: internet_search\n", + " type: integration\n", + " integration:\n", + " provider: brave\n", + " setup:\n", + " api_key: {brave_api_key}\n", + "\n", + "main:\n", + "- over: inputs[0].locations\n", + " map:\n", + " tool: weather\n", + " arguments:\n", + " location: _\n", + "\n", + "- over: inputs[0].locations\n", + " map:\n", + " tool: internet_search\n", + " arguments:\n", + " query: \"'tourist attractions in ' + _\"\n", + "\n", + "# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]\n", + "- evaluate:\n", + " zipped: |-\n", + " list(\n", + " zip(\n", + " inputs[0].locations,\n", + " [output['result'] for output in outputs[0]],\n", + " outputs[1]\n", + " )\n", + " )\n", + "\n", + "\n", + "- over: _['zipped']\n", + " parallelism: 3\n", + " # Inside the map step, each `_` represents the current element in the list\n", + " # which is a tuple of (location, weather, attractions)\n", + " map:\n", + " prompt:\n", + " - role: system\n", + " content: >-\n", + " You are {{{{agent.name}}}}. Your task is to create a detailed itinerary\n", + " for visiting tourist attractions in some locations.\n", + " The user will give you the following information for each location:\n", + "\n", + " - The location\n", + " - The current weather condition\n", + " - The top tourist attractions\n", + " - role: user\n", + " content: >-\n", + " Location: \"{{{{_[0]}}}}\"\n", + " Weather: \"{{{{_[1]}}}}\"\n", + " Attractions: \"{{{{_[2]}}}}\"\n", + " unwrap: true\n", + "\n", + "- evaluate:\n", + " final_plan: |-\n", + " '\\\\n---------------\\\\n'.join(activity for activity in _)\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# creating the task object\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started an execution. Execution ID: 240c3e5e-a1a5-4d2c-aa24-ee86e876139e\n" + ] + } + ], + "source": [ + "execution = client.executions.create(\n", + " task_id=task.id,\n", + " input={\n", + " \"locations\": [\"New York\", \"London\", \"Paris\", \"Tokyo\", \"Sydney\"]\n", + " }\n", + ")\n", + "\n", + "print(\"Started an execution. Execution ID:\", execution.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'final_plan': \"Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions. \\n\\n### Day 1: Iconic Landmarks and Observation Decks\\n- **Morning:**\\n - **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.\\n - **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)\\n\\n- **Afternoon:**\\n - **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.\\n - **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.\\n\\n- **Evening:**\\n - **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.\\n\\n### Day 2: Culture and History\\n- **Morning:**\\n - **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.\\n\\n- **Afternoon:**\\n - **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.\\n\\n- **Evening:**\\n - **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.\\n\\n### Day 3: Historical and Educational\\n- **Morning:**\\n - **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.\\n\\n- **Afternoon:**\\n - **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.\\n\\n- **Evening:**\\n - **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.\\n\\n### Additional Tips:\\n- **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.\\n- **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.\\n- **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.\\n\\nFor more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/). \\n\\nEnjoy your trip to New York City!\\n---------------\\n**Day 1: Exploring Iconic London Landmarks**\\n\\n**Morning:**\\n1. **Buckingham Palace**\\n - Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.\\n - Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.\\n\\n**Midday:**\\n2. **Westminster Abbey**\\n - Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.\\n\\n3. **Lunch at Borough Market**\\n - Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.\\n\\n**Afternoon:**\\n4. **London Eye**\\n - After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.\\n\\n**Evening:**\\n5. **The Globe Theatre**\\n - As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.\\n\\n**Day 2: Museums and Cultural Exploration**\\n\\n**Morning:**\\n1. **The British Museum**\\n - Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.\\n\\n**Midday:**\\n2. **Lunch near Covent Garden**\\n - Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.\\n\\n**Afternoon:**\\n3. **The Tower of London**\\n - After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.\\n\\n**Evening:**\\n4. **Tower Bridge**\\n - Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.\\n\\n**Day 3: Leisure and Local Experiences**\\n\\n**Morning:**\\n1. **Natural History Museum**\\n - Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.\\n\\n**Midday:**\\n2. **Lunch at South Kensington**\\n - Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.\\n\\n**Afternoon:**\\n3. **Hyde Park**\\n - Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.\\n\\n**Evening:**\\n4. **Dinner and a Show in the West End**\\n - End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.\\n\\n**Additional Tips:**\\n- Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.\\n- London’s public transport system is efficient; consider getting an Oyster card for convenient travel.\\n- Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.\\n---------------\\nHere’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:\\n\\n### Day 1: Embrace the Iconic Landmarks\\n\\n**Morning: Eiffel Tower**\\n- **Time:** 9:00 AM\\n- **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.\\n- **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)\\n\\n**Afternoon: Louvre Museum**\\n- **Time:** 1:00 PM\\n- **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.\\n- **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)\\n\\n**Evening: Seine River Cruise**\\n- **Time:** 6:00 PM\\n- **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.\\n\\n### Day 2: Explore Cultural and Historical Treasures\\n\\n**Morning: Notre-Dame Cathedral**\\n- **Time:** 9:30 AM\\n- **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.\\n\\n**Afternoon: Musée d'Orsay**\\n- **Time:** 1:30 PM\\n- **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.\\n\\n**Evening: Montmartre and Sacré-Cœur**\\n- **Time:** 5:00 PM\\n- **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.\\n\\n### Day 3: Discover Hidden Gems and Local Flavors\\n\\n**Morning: Le Marais District**\\n- **Time:** 10:00 AM\\n- **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.\\n\\n**Afternoon: Palais Garnier (Opera House)**\\n- **Time:** 2:00 PM\\n- **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.\\n\\n**Evening: Moulin Rouge Show**\\n- **Time:** 8:00 PM\\n- **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.\\n\\n### Additional Tips:\\n- **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.\\n- **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.\\n- **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.\\n\\nEnjoy your snowy adventure in Paris!\\n---------------\\n**Tokyo Itinerary**\\n\\n**Day 1: Arrival and Exploration of Historical and Cultural Sites**\\n\\n- **Morning:**\\n - **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.\\n\\n- **Afternoon:**\\n - **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.\\n\\n- **Evening:**\\n - **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.\\n\\n**Day 2: Modern Tokyo and Unique Experiences**\\n\\n- **Morning:**\\n - **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.\\n\\n- **Afternoon:**\\n - **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.\\n\\n- **Evening:**\\n - **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.\\n\\n**Day 3: Relax and Explore Green Spaces**\\n\\n- **Morning:**\\n - **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.\\n\\n- **Afternoon:**\\n - **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.\\n\\n- **Evening:**\\n - **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.\\n\\n**Weather Considerations:**\\n- With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.\\n- Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.\\n\\n**Additional Tips:**\\n- Always check the opening hours of attractions and book tickets in advance where necessary.\\n- Use Tokyo’s efficient public transport to move around easily.\\n- Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.\\n---------------\\nHere's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.\\n\\n### Morning\\n\\n**9:00 AM - Sydney Opera House**\\n- Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**11:00 AM - Royal Botanic Garden Sydney**\\n- Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.\\n\\n### Afternoon\\n\\n**12:30 PM - Lunch at Opera Bar**\\n- Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**2:00 PM - Sydney Harbour Bridge**\\n- After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.\\n\\n**4:00 PM - The Rocks**\\n- Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.\\n\\n### Evening\\n\\n**6:00 PM - Darling Harbour**\\n- Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.\\n\\n**8:00 PM - Dinner at a Local Restaurant**\\n- Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.\\n\\n### Additional Suggestions\\n\\n- If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.\\n\\nWith clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!\"}\n", + "--------------------------------------------------\n", + "Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions. \n", + "\n", + "### Day 1: Iconic Landmarks and Observation Decks\n", + "- **Morning:**\n", + " - **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.\n", + " - **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)\n", + "\n", + "- **Afternoon:**\n", + " - **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.\n", + " - **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.\n", + "\n", + "- **Evening:**\n", + " - **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.\n", + "\n", + "### Day 2: Culture and History\n", + "- **Morning:**\n", + " - **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.\n", + "\n", + "- **Afternoon:**\n", + " - **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.\n", + "\n", + "- **Evening:**\n", + " - **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.\n", + "\n", + "### Day 3: Historical and Educational\n", + "- **Morning:**\n", + " - **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.\n", + "\n", + "- **Afternoon:**\n", + " - **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.\n", + "\n", + "- **Evening:**\n", + " - **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.\n", + "\n", + "### Additional Tips:\n", + "- **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.\n", + "- **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.\n", + "- **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.\n", + "\n", + "For more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/). \n", + "\n", + "Enjoy your trip to New York City!\n", + "---------------\n", + "**Day 1: Exploring Iconic London Landmarks**\n", + "\n", + "**Morning:**\n", + "1. **Buckingham Palace**\n", + " - Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.\n", + " - Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.\n", + "\n", + "**Midday:**\n", + "2. **Westminster Abbey**\n", + " - Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.\n", + "\n", + "3. **Lunch at Borough Market**\n", + " - Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.\n", + "\n", + "**Afternoon:**\n", + "4. **London Eye**\n", + " - After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.\n", + "\n", + "**Evening:**\n", + "5. **The Globe Theatre**\n", + " - As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.\n", + "\n", + "**Day 2: Museums and Cultural Exploration**\n", + "\n", + "**Morning:**\n", + "1. **The British Museum**\n", + " - Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.\n", + "\n", + "**Midday:**\n", + "2. **Lunch near Covent Garden**\n", + " - Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.\n", + "\n", + "**Afternoon:**\n", + "3. **The Tower of London**\n", + " - After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.\n", + "\n", + "**Evening:**\n", + "4. **Tower Bridge**\n", + " - Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.\n", + "\n", + "**Day 3: Leisure and Local Experiences**\n", + "\n", + "**Morning:**\n", + "1. **Natural History Museum**\n", + " - Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.\n", + "\n", + "**Midday:**\n", + "2. **Lunch at South Kensington**\n", + " - Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.\n", + "\n", + "**Afternoon:**\n", + "3. **Hyde Park**\n", + " - Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.\n", + "\n", + "**Evening:**\n", + "4. **Dinner and a Show in the West End**\n", + " - End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.\n", + "\n", + "**Additional Tips:**\n", + "- Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.\n", + "- London’s public transport system is efficient; consider getting an Oyster card for convenient travel.\n", + "- Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.\n", + "---------------\n", + "Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:\n", + "\n", + "### Day 1: Embrace the Iconic Landmarks\n", + "\n", + "**Morning: Eiffel Tower**\n", + "- **Time:** 9:00 AM\n", + "- **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.\n", + "- **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)\n", + "\n", + "**Afternoon: Louvre Museum**\n", + "- **Time:** 1:00 PM\n", + "- **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.\n", + "- **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)\n", + "\n", + "**Evening: Seine River Cruise**\n", + "- **Time:** 6:00 PM\n", + "- **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.\n", + "\n", + "### Day 2: Explore Cultural and Historical Treasures\n", + "\n", + "**Morning: Notre-Dame Cathedral**\n", + "- **Time:** 9:30 AM\n", + "- **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.\n", + "\n", + "**Afternoon: Musée d'Orsay**\n", + "- **Time:** 1:30 PM\n", + "- **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.\n", + "\n", + "**Evening: Montmartre and Sacré-Cœur**\n", + "- **Time:** 5:00 PM\n", + "- **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.\n", + "\n", + "### Day 3: Discover Hidden Gems and Local Flavors\n", + "\n", + "**Morning: Le Marais District**\n", + "- **Time:** 10:00 AM\n", + "- **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.\n", + "\n", + "**Afternoon: Palais Garnier (Opera House)**\n", + "- **Time:** 2:00 PM\n", + "- **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.\n", + "\n", + "**Evening: Moulin Rouge Show**\n", + "- **Time:** 8:00 PM\n", + "- **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.\n", + "\n", + "### Additional Tips:\n", + "- **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.\n", + "- **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.\n", + "- **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.\n", + "\n", + "Enjoy your snowy adventure in Paris!\n", + "---------------\n", + "**Tokyo Itinerary**\n", + "\n", + "**Day 1: Arrival and Exploration of Historical and Cultural Sites**\n", + "\n", + "- **Morning:**\n", + " - **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.\n", + "\n", + "- **Afternoon:**\n", + " - **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.\n", + "\n", + "- **Evening:**\n", + " - **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.\n", + "\n", + "**Day 2: Modern Tokyo and Unique Experiences**\n", + "\n", + "- **Morning:**\n", + " - **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.\n", + "\n", + "- **Afternoon:**\n", + " - **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.\n", + "\n", + "- **Evening:**\n", + " - **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.\n", + "\n", + "**Day 3: Relax and Explore Green Spaces**\n", + "\n", + "- **Morning:**\n", + " - **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.\n", + "\n", + "- **Afternoon:**\n", + " - **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.\n", + "\n", + "- **Evening:**\n", + " - **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.\n", + "\n", + "**Weather Considerations:**\n", + "- With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.\n", + "- Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.\n", + "\n", + "**Additional Tips:**\n", + "- Always check the opening hours of attractions and book tickets in advance where necessary.\n", + "- Use Tokyo’s efficient public transport to move around easily.\n", + "- Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.\n", + "---------------\n", + "Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.\n", + "\n", + "### Morning\n", + "\n", + "**9:00 AM - Sydney Opera House**\n", + "- Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.\n", + "- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\n", + "\n", + "**11:00 AM - Royal Botanic Garden Sydney**\n", + "- Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.\n", + "\n", + "### Afternoon\n", + "\n", + "**12:30 PM - Lunch at Opera Bar**\n", + "- Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.\n", + "- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\n", + "\n", + "**2:00 PM - Sydney Harbour Bridge**\n", + "- After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.\n", + "\n", + "**4:00 PM - The Rocks**\n", + "- Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.\n", + "\n", + "### Evening\n", + "\n", + "**6:00 PM - Darling Harbour**\n", + "- Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.\n", + "\n", + "**8:00 PM - Dinner at a Local Restaurant**\n", + "- Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.\n", + "\n", + "### Additional Suggestions\n", + "\n", + "- If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.\n", + "\n", + "With clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!\n" + ] + } + ], + "source": [ + "# Get execution details\n", + "execution = client.executions.get(execution.id)\n", + "# Print the output\n", + "print(execution.output)\n", + "print(\"-\"*50)\n", + "\n", + "if 'final_plan' in execution.output:\n", + " print(execution.output['final_plan'])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition type: init\n", + "Transition output: {'locations': ['New York', 'London', 'Paris', 'Tokyo', 'Sydney']}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: New York\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': 'In New York, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 5.81 m/s, direction: 22°\\nHumidity: 90%\\nTemperature: \\n - Current: 8.34°C\\n - High: 9.01°C\\n - Low: 7.16°C\\n - Feels like: 5.2°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: London\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': 'In London, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 2.57 m/s, direction: 350°\\nHumidity: 72%\\nTemperature: \\n - Current: 3.42°C\\n - High: 3.97°C\\n - Low: 2.79°C\\n - Feels like: 0.97°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Paris\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': 'In Paris, the current weather is as follows:\\nDetailed status: snow\\nWind speed: 9.26 m/s, direction: 70°\\nHumidity: 99%\\nTemperature: \\n - Current: 0.57°C\\n - High: 1.16°C\\n - Low: -0.35°C\\n - Feels like: -6.04°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Tokyo\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': 'In Tokyo, the current weather is as follows:\\nDetailed status: few clouds\\nWind speed: 2.57 m/s, direction: 280°\\nHumidity: 92%\\nTemperature: \\n - Current: 10.32°C\\n - High: 10.91°C\\n - Low: 9.19°C\\n - Feels like: 9.81°C\\nRain: {}\\nHeat index: None\\nCloud cover: 20%'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Sydney\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': 'In Sydney, the current weather is as follows:\\nDetailed status: clear sky\\nWind speed: 2.93 m/s, direction: 348°\\nHumidity: 81%\\nTemperature: \\n - Current: 16.95°C\\n - High: 18.04°C\\n - Low: 15.42°C\\n - Feels like: 16.82°C\\nRain: {}\\nHeat index: None\\nCloud cover: 0%'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: [{'result': 'In New York, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 5.81 m/s, direction: 22°\\nHumidity: 90%\\nTemperature: \\n - Current: 8.34°C\\n - High: 9.01°C\\n - Low: 7.16°C\\n - Feels like: 5.2°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}, {'result': 'In London, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 2.57 m/s, direction: 350°\\nHumidity: 72%\\nTemperature: \\n - Current: 3.42°C\\n - High: 3.97°C\\n - Low: 2.79°C\\n - Feels like: 0.97°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}, {'result': 'In Paris, the current weather is as follows:\\nDetailed status: snow\\nWind speed: 9.26 m/s, direction: 70°\\nHumidity: 99%\\nTemperature: \\n - Current: 0.57°C\\n - High: 1.16°C\\n - Low: -0.35°C\\n - Feels like: -6.04°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%'}, {'result': 'In Tokyo, the current weather is as follows:\\nDetailed status: few clouds\\nWind speed: 2.57 m/s, direction: 280°\\nHumidity: 92%\\nTemperature: \\n - Current: 10.32°C\\n - High: 10.91°C\\n - Low: 9.19°C\\n - Feels like: 9.81°C\\nRain: {}\\nHeat index: None\\nCloud cover: 20%'}, {'result': 'In Sydney, the current weather is as follows:\\nDetailed status: clear sky\\nWind speed: 2.93 m/s, direction: 348°\\nHumidity: 81%\\nTemperature: \\n - Current: 16.95°C\\n - High: 18.04°C\\n - Low: 15.42°C\\n - Feels like: 16.82°C\\nRain: {}\\nHeat index: None\\nCloud cover: 0%'}]\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: New York\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html', 'snippet': 'Top of the Rock Observation Deck, the newly opened, 3-tiered observation deck on the 67th, 69th and 70th floors of 30 Rockefeller Plaza, is New York City's most amazing attraction! The unforgettable experience includes a panoramic 360-degree, unobstructed view from the 70th floor observatory, ...', 'title': 'THE 15 BEST Things to Do in NYC - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.iloveny.com/places-to-go/new-york-city/attractions/', 'snippet': 'Whether you're into architecture, Broadway theater, restaurants or other cultural hotspots, New York City has something for you. Plan your trip today!', 'title': 'NYC Attractions | Museums, Parks, Zoos & Landmarks'}, {'link': 'https://usaguidedtours.com/nyc/attraction/', 'snippet': 'Learn more about New York City attractions or simply book one of our award-winning guided tours and explore them up-close and personal!', 'title': 'New York Attractions - USA Guided Tours'}]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: London\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g186338-Activities-London_England.html', 'snippet': 'Things to Do in London, England: See Tripadvisor's 7,202,262 traveler reviews and photos of London tourist attractions. Find what to do today, this weekend, or in August. We have reviews of the best places to see in London. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in London (2024) - Must-See Attractions'}, {'link': 'https://www.visitlondon.com/things-to-do/sightseeing/london-attraction', 'snippet': 'Find the best London tourist attractions, including museums, palaces and London landmarks. Pick things to see in London, attractions or take a sightseeing tour.', 'title': \"London attractions | Visit London's top tourist attractions\"}, {'link': 'https://www.timeout.com/london/attractions/top-london-attractions', 'snippet': 'Discover the best, most unmissable attractions in London, including Buckingham Palace, The Globe, the London Eye and more.', 'title': '50 Best Attractions in London for 2024 | Best Things to Do in London'}]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Paris\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html', 'snippet': 'Things to Do in Paris, France: See Tripadvisor's 5,011,626 traveler reviews and photos of Paris tourist attractions. Find what to do today, this weekend, or in September. We have reviews of the best places to see in Paris. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Paris (2024) - Must-See Attractions'}, {'link': 'https://www.timeout.com/paris/en/attractions/best-paris-attractions', 'snippet': 'And we’re not alone in thinking that. Paris is a major tourist destination that attracts thousands upon thousands of enthusiastic travellers with heads filled with images of Breton jumpers, tiny dogs, and decadent pastries - the kind you can dip in your hot chocolate.', 'title': '51 Best Attractions in Paris for 2024 | Best Sites to See in Paris'}, {'link': 'https://travel.usnews.com/Paris_France/Things_To_Do/', 'snippet': 'If you've been dreaming of a visit to the Eiffel Tower, Louvre Museum and Moulin Rouge, plan your trip with these expert recommendations for things to do in Paris.', 'title': '32 Best Things to Do in Paris, France | U.S. News Travel'}]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Tokyo\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g298184-Activities-Tokyo_Tokyo_Prefecture_Kanto.html', 'snippet': 'Things to Do in Tokyo, Japan: See Tripadvisor's 1,602,397 traveler reviews and photos of Tokyo tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Tokyo. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Tokyo - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.lonelyplanet.com/japan/tokyo/attractions', 'snippet': 'Discover the best attractions in Tokyo including Tokyo National Museum, Ghibli Museum, and Golden Gai.', 'title': 'Must-see attractions Tokyo, Japan - Lonely Planet'}, {'link': 'https://www.japan-guide.com/e/e2164.html', 'snippet': 'The city's history can be appreciated in districts such as Asakusa and in many excellent museums, historic temples and gardens. Contrary to common perception, Tokyo also offers a number of attractive green spaces in the city center and within relatively short train rides at its outskirts.', 'title': 'Tokyo City Guide - What to do in Tokyo'}]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: Sydney\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g255060-Activities-Sydney_New_South_Wales.html', 'snippet': 'Things to Do in Sydney, Australia: See Tripadvisor's 1,043,552 traveler reviews and photos of Sydney tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Sydney. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Sydney - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.sydney.com/things-to-do/attractions', 'snippet': 'The UNESCO World Heritage-listed building offers daily guided tours, available in English, Mandarin, French, German, Japanese, Korean and Spanish. Meanwhile, Opera Bar has the best view in town, letting you sip cocktails as you marvel at the Sydney Harbour Bridge.', 'title': 'Top attractions in Sydney | Sydney.com'}, {'link': 'https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck', 'snippet': 'We’ve rounded up 19 infamous Sydney tourist stops that you'll actually enjoy, from thrilling adventures to scenic tours.', 'title': \"19 Amazing Tourist Attractions in Sydney That Don't Suck\"}]}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: [{'result': [{'link': 'https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html', 'snippet': 'Top of the Rock Observation Deck, the newly opened, 3-tiered observation deck on the 67th, 69th and 70th floors of 30 Rockefeller Plaza, is New York City's most amazing attraction! The unforgettable experience includes a panoramic 360-degree, unobstructed view from the 70th floor observatory, ...', 'title': 'THE 15 BEST Things to Do in NYC - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.iloveny.com/places-to-go/new-york-city/attractions/', 'snippet': 'Whether you're into architecture, Broadway theater, restaurants or other cultural hotspots, New York City has something for you. Plan your trip today!', 'title': 'NYC Attractions | Museums, Parks, Zoos & Landmarks'}, {'link': 'https://usaguidedtours.com/nyc/attraction/', 'snippet': 'Learn more about New York City attractions or simply book one of our award-winning guided tours and explore them up-close and personal!', 'title': 'New York Attractions - USA Guided Tours'}]}, {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g186338-Activities-London_England.html', 'snippet': 'Things to Do in London, England: See Tripadvisor's 7,202,262 traveler reviews and photos of London tourist attractions. Find what to do today, this weekend, or in August. We have reviews of the best places to see in London. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in London (2024) - Must-See Attractions'}, {'link': 'https://www.visitlondon.com/things-to-do/sightseeing/london-attraction', 'snippet': 'Find the best London tourist attractions, including museums, palaces and London landmarks. Pick things to see in London, attractions or take a sightseeing tour.', 'title': \"London attractions | Visit London's top tourist attractions\"}, {'link': 'https://www.timeout.com/london/attractions/top-london-attractions', 'snippet': 'Discover the best, most unmissable attractions in London, including Buckingham Palace, The Globe, the London Eye and more.', 'title': '50 Best Attractions in London for 2024 | Best Things to Do in London'}]}, {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html', 'snippet': 'Things to Do in Paris, France: See Tripadvisor's 5,011,626 traveler reviews and photos of Paris tourist attractions. Find what to do today, this weekend, or in September. We have reviews of the best places to see in Paris. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Paris (2024) - Must-See Attractions'}, {'link': 'https://www.timeout.com/paris/en/attractions/best-paris-attractions', 'snippet': 'And we’re not alone in thinking that. Paris is a major tourist destination that attracts thousands upon thousands of enthusiastic travellers with heads filled with images of Breton jumpers, tiny dogs, and decadent pastries - the kind you can dip in your hot chocolate.', 'title': '51 Best Attractions in Paris for 2024 | Best Sites to See in Paris'}, {'link': 'https://travel.usnews.com/Paris_France/Things_To_Do/', 'snippet': 'If you've been dreaming of a visit to the Eiffel Tower, Louvre Museum and Moulin Rouge, plan your trip with these expert recommendations for things to do in Paris.', 'title': '32 Best Things to Do in Paris, France | U.S. News Travel'}]}, {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g298184-Activities-Tokyo_Tokyo_Prefecture_Kanto.html', 'snippet': 'Things to Do in Tokyo, Japan: See Tripadvisor's 1,602,397 traveler reviews and photos of Tokyo tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Tokyo. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Tokyo - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.lonelyplanet.com/japan/tokyo/attractions', 'snippet': 'Discover the best attractions in Tokyo including Tokyo National Museum, Ghibli Museum, and Golden Gai.', 'title': 'Must-see attractions Tokyo, Japan - Lonely Planet'}, {'link': 'https://www.japan-guide.com/e/e2164.html', 'snippet': 'The city's history can be appreciated in districts such as Asakusa and in many excellent museums, historic temples and gardens. Contrary to common perception, Tokyo also offers a number of attractive green spaces in the city center and within relatively short train rides at its outskirts.', 'title': 'Tokyo City Guide - What to do in Tokyo'}]}, {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g255060-Activities-Sydney_New_South_Wales.html', 'snippet': 'Things to Do in Sydney, Australia: See Tripadvisor's 1,043,552 traveler reviews and photos of Sydney tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Sydney. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Sydney - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.sydney.com/things-to-do/attractions', 'snippet': 'The UNESCO World Heritage-listed building offers daily guided tours, available in English, Mandarin, French, German, Japanese, Korean and Spanish. Meanwhile, Opera Bar has the best view in town, letting you sip cocktails as you marvel at the Sydney Harbour Bridge.', 'title': 'Top attractions in Sydney | Sydney.com'}, {'link': 'https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck', 'snippet': 'We’ve rounded up 19 infamous Sydney tourist stops that you'll actually enjoy, from thrilling adventures to scenic tours.', 'title': \"19 Amazing Tourist Attractions in Sydney That Don't Suck\"}]}]\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'zipped': [['New York', 'In New York, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 5.81 m/s, direction: 22°\\nHumidity: 90%\\nTemperature: \\n - Current: 8.34°C\\n - High: 9.01°C\\n - Low: 7.16°C\\n - Feels like: 5.2°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html', 'snippet': 'Top of the Rock Observation Deck, the newly opened, 3-tiered observation deck on the 67th, 69th and 70th floors of 30 Rockefeller Plaza, is New York City's most amazing attraction! The unforgettable experience includes a panoramic 360-degree, unobstructed view from the 70th floor observatory, ...', 'title': 'THE 15 BEST Things to Do in NYC - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.iloveny.com/places-to-go/new-york-city/attractions/', 'snippet': 'Whether you're into architecture, Broadway theater, restaurants or other cultural hotspots, New York City has something for you. Plan your trip today!', 'title': 'NYC Attractions | Museums, Parks, Zoos & Landmarks'}, {'link': 'https://usaguidedtours.com/nyc/attraction/', 'snippet': 'Learn more about New York City attractions or simply book one of our award-winning guided tours and explore them up-close and personal!', 'title': 'New York Attractions - USA Guided Tours'}]}], ['London', 'In London, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 2.57 m/s, direction: 350°\\nHumidity: 72%\\nTemperature: \\n - Current: 3.42°C\\n - High: 3.97°C\\n - Low: 2.79°C\\n - Feels like: 0.97°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g186338-Activities-London_England.html', 'snippet': 'Things to Do in London, England: See Tripadvisor's 7,202,262 traveler reviews and photos of London tourist attractions. Find what to do today, this weekend, or in August. We have reviews of the best places to see in London. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in London (2024) - Must-See Attractions'}, {'link': 'https://www.visitlondon.com/things-to-do/sightseeing/london-attraction', 'snippet': 'Find the best London tourist attractions, including museums, palaces and London landmarks. Pick things to see in London, attractions or take a sightseeing tour.', 'title': \"London attractions | Visit London's top tourist attractions\"}, {'link': 'https://www.timeout.com/london/attractions/top-london-attractions', 'snippet': 'Discover the best, most unmissable attractions in London, including Buckingham Palace, The Globe, the London Eye and more.', 'title': '50 Best Attractions in London for 2024 | Best Things to Do in London'}]}], ['Paris', 'In Paris, the current weather is as follows:\\nDetailed status: snow\\nWind speed: 9.26 m/s, direction: 70°\\nHumidity: 99%\\nTemperature: \\n - Current: 0.57°C\\n - High: 1.16°C\\n - Low: -0.35°C\\n - Feels like: -6.04°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html', 'snippet': 'Things to Do in Paris, France: See Tripadvisor's 5,011,626 traveler reviews and photos of Paris tourist attractions. Find what to do today, this weekend, or in September. We have reviews of the best places to see in Paris. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Paris (2024) - Must-See Attractions'}, {'link': 'https://www.timeout.com/paris/en/attractions/best-paris-attractions', 'snippet': 'And we’re not alone in thinking that. Paris is a major tourist destination that attracts thousands upon thousands of enthusiastic travellers with heads filled with images of Breton jumpers, tiny dogs, and decadent pastries - the kind you can dip in your hot chocolate.', 'title': '51 Best Attractions in Paris for 2024 | Best Sites to See in Paris'}, {'link': 'https://travel.usnews.com/Paris_France/Things_To_Do/', 'snippet': 'If you've been dreaming of a visit to the Eiffel Tower, Louvre Museum and Moulin Rouge, plan your trip with these expert recommendations for things to do in Paris.', 'title': '32 Best Things to Do in Paris, France | U.S. News Travel'}]}], ['Tokyo', 'In Tokyo, the current weather is as follows:\\nDetailed status: few clouds\\nWind speed: 2.57 m/s, direction: 280°\\nHumidity: 92%\\nTemperature: \\n - Current: 10.32°C\\n - High: 10.91°C\\n - Low: 9.19°C\\n - Feels like: 9.81°C\\nRain: {}\\nHeat index: None\\nCloud cover: 20%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g298184-Activities-Tokyo_Tokyo_Prefecture_Kanto.html', 'snippet': 'Things to Do in Tokyo, Japan: See Tripadvisor's 1,602,397 traveler reviews and photos of Tokyo tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Tokyo. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Tokyo - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.lonelyplanet.com/japan/tokyo/attractions', 'snippet': 'Discover the best attractions in Tokyo including Tokyo National Museum, Ghibli Museum, and Golden Gai.', 'title': 'Must-see attractions Tokyo, Japan - Lonely Planet'}, {'link': 'https://www.japan-guide.com/e/e2164.html', 'snippet': 'The city's history can be appreciated in districts such as Asakusa and in many excellent museums, historic temples and gardens. Contrary to common perception, Tokyo also offers a number of attractive green spaces in the city center and within relatively short train rides at its outskirts.', 'title': 'Tokyo City Guide - What to do in Tokyo'}]}], ['Sydney', 'In Sydney, the current weather is as follows:\\nDetailed status: clear sky\\nWind speed: 2.93 m/s, direction: 348°\\nHumidity: 81%\\nTemperature: \\n - Current: 16.95°C\\n - High: 18.04°C\\n - Low: 15.42°C\\n - Feels like: 16.82°C\\nRain: {}\\nHeat index: None\\nCloud cover: 0%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g255060-Activities-Sydney_New_South_Wales.html', 'snippet': 'Things to Do in Sydney, Australia: See Tripadvisor's 1,043,552 traveler reviews and photos of Sydney tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Sydney. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Sydney - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.sydney.com/things-to-do/attractions', 'snippet': 'The UNESCO World Heritage-listed building offers daily guided tours, available in English, Mandarin, French, German, Japanese, Korean and Spanish. Meanwhile, Opera Bar has the best view in town, letting you sip cocktails as you marvel at the Sydney Harbour Bridge.', 'title': 'Top attractions in Sydney | Sydney.com'}, {'link': 'https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck', 'snippet': 'We’ve rounded up 19 infamous Sydney tourist stops that you'll actually enjoy, from thrilling adventures to scenic tours.', 'title': \"19 Amazing Tourist Attractions in Sydney That Don't Suck\"}]}]]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: ['London', 'In London, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 2.57 m/s, direction: 350°\\nHumidity: 72%\\nTemperature: \\n - Current: 3.42°C\\n - High: 3.97°C\\n - Low: 2.79°C\\n - Feels like: 0.97°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g186338-Activities-London_England.html', 'snippet': 'Things to Do in London, England: See Tripadvisor's 7,202,262 traveler reviews and photos of London tourist attractions. Find what to do today, this weekend, or in August. We have reviews of the best places to see in London. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in London (2024) - Must-See Attractions'}, {'link': 'https://www.visitlondon.com/things-to-do/sightseeing/london-attraction', 'snippet': 'Find the best London tourist attractions, including museums, palaces and London landmarks. Pick things to see in London, attractions or take a sightseeing tour.', 'title': \"London attractions | Visit London's top tourist attractions\"}, {'link': 'https://www.timeout.com/london/attractions/top-london-attractions', 'snippet': 'Discover the best, most unmissable attractions in London, including Buckingham Palace, The Globe, the London Eye and more.', 'title': '50 Best Attractions in London for 2024 | Best Things to Do in London'}]}]\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: ['New York', 'In New York, the current weather is as follows:\\nDetailed status: overcast clouds\\nWind speed: 5.81 m/s, direction: 22°\\nHumidity: 90%\\nTemperature: \\n - Current: 8.34°C\\n - High: 9.01°C\\n - Low: 7.16°C\\n - Feels like: 5.2°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html', 'snippet': 'Top of the Rock Observation Deck, the newly opened, 3-tiered observation deck on the 67th, 69th and 70th floors of 30 Rockefeller Plaza, is New York City's most amazing attraction! The unforgettable experience includes a panoramic 360-degree, unobstructed view from the 70th floor observatory, ...', 'title': 'THE 15 BEST Things to Do in NYC - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.iloveny.com/places-to-go/new-york-city/attractions/', 'snippet': 'Whether you're into architecture, Broadway theater, restaurants or other cultural hotspots, New York City has something for you. Plan your trip today!', 'title': 'NYC Attractions | Museums, Parks, Zoos & Landmarks'}, {'link': 'https://usaguidedtours.com/nyc/attraction/', 'snippet': 'Learn more about New York City attractions or simply book one of our award-winning guided tours and explore them up-close and personal!', 'title': 'New York Attractions - USA Guided Tours'}]}]\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: ['Paris', 'In Paris, the current weather is as follows:\\nDetailed status: snow\\nWind speed: 9.26 m/s, direction: 70°\\nHumidity: 99%\\nTemperature: \\n - Current: 0.57°C\\n - High: 1.16°C\\n - Low: -0.35°C\\n - Feels like: -6.04°C\\nRain: {}\\nHeat index: None\\nCloud cover: 100%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html', 'snippet': 'Things to Do in Paris, France: See Tripadvisor's 5,011,626 traveler reviews and photos of Paris tourist attractions. Find what to do today, this weekend, or in September. We have reviews of the best places to see in Paris. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Paris (2024) - Must-See Attractions'}, {'link': 'https://www.timeout.com/paris/en/attractions/best-paris-attractions', 'snippet': 'And we’re not alone in thinking that. Paris is a major tourist destination that attracts thousands upon thousands of enthusiastic travellers with heads filled with images of Breton jumpers, tiny dogs, and decadent pastries - the kind you can dip in your hot chocolate.', 'title': '51 Best Attractions in Paris for 2024 | Best Sites to See in Paris'}, {'link': 'https://travel.usnews.com/Paris_France/Things_To_Do/', 'snippet': 'If you've been dreaming of a visit to the Eiffel Tower, Louvre Museum and Moulin Rouge, plan your trip with these expert recommendations for things to do in Paris.', 'title': '32 Best Things to Do in Paris, France | U.S. News Travel'}]}]\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:\n", + "\n", + "### Day 1: Embrace the Iconic Landmarks\n", + "\n", + "**Morning: Eiffel Tower**\n", + "- **Time:** 9:00 AM\n", + "- **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.\n", + "- **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)\n", + "\n", + "**Afternoon: Louvre Museum**\n", + "- **Time:** 1:00 PM\n", + "- **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.\n", + "- **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)\n", + "\n", + "**Evening: Seine River Cruise**\n", + "- **Time:** 6:00 PM\n", + "- **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.\n", + "\n", + "### Day 2: Explore Cultural and Historical Treasures\n", + "\n", + "**Morning: Notre-Dame Cathedral**\n", + "- **Time:** 9:30 AM\n", + "- **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.\n", + "\n", + "**Afternoon: Musée d'Orsay**\n", + "- **Time:** 1:30 PM\n", + "- **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.\n", + "\n", + "**Evening: Montmartre and Sacré-Cœur**\n", + "- **Time:** 5:00 PM\n", + "- **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.\n", + "\n", + "### Day 3: Discover Hidden Gems and Local Flavors\n", + "\n", + "**Morning: Le Marais District**\n", + "- **Time:** 10:00 AM\n", + "- **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.\n", + "\n", + "**Afternoon: Palais Garnier (Opera House)**\n", + "- **Time:** 2:00 PM\n", + "- **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.\n", + "\n", + "**Evening: Moulin Rouge Show**\n", + "- **Time:** 8:00 PM\n", + "- **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.\n", + "\n", + "### Additional Tips:\n", + "- **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.\n", + "- **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.\n", + "- **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.\n", + "\n", + "Enjoy your snowy adventure in Paris!\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions. \n", + "\n", + "### Day 1: Iconic Landmarks and Observation Decks\n", + "- **Morning:**\n", + " - **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.\n", + " - **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)\n", + "\n", + "- **Afternoon:**\n", + " - **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.\n", + " - **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.\n", + "\n", + "- **Evening:**\n", + " - **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.\n", + "\n", + "### Day 2: Culture and History\n", + "- **Morning:**\n", + " - **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.\n", + "\n", + "- **Afternoon:**\n", + " - **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.\n", + "\n", + "- **Evening:**\n", + " - **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.\n", + "\n", + "### Day 3: Historical and Educational\n", + "- **Morning:**\n", + " - **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.\n", + "\n", + "- **Afternoon:**\n", + " - **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.\n", + "\n", + "- **Evening:**\n", + " - **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.\n", + "\n", + "### Additional Tips:\n", + "- **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.\n", + "- **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.\n", + "- **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.\n", + "\n", + "For more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/). \n", + "\n", + "Enjoy your trip to New York City!\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: **Day 1: Exploring Iconic London Landmarks**\n", + "\n", + "**Morning:**\n", + "1. **Buckingham Palace**\n", + " - Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.\n", + " - Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.\n", + "\n", + "**Midday:**\n", + "2. **Westminster Abbey**\n", + " - Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.\n", + "\n", + "3. **Lunch at Borough Market**\n", + " - Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.\n", + "\n", + "**Afternoon:**\n", + "4. **London Eye**\n", + " - After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.\n", + "\n", + "**Evening:**\n", + "5. **The Globe Theatre**\n", + " - As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.\n", + "\n", + "**Day 2: Museums and Cultural Exploration**\n", + "\n", + "**Morning:**\n", + "1. **The British Museum**\n", + " - Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.\n", + "\n", + "**Midday:**\n", + "2. **Lunch near Covent Garden**\n", + " - Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.\n", + "\n", + "**Afternoon:**\n", + "3. **The Tower of London**\n", + " - After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.\n", + "\n", + "**Evening:**\n", + "4. **Tower Bridge**\n", + " - Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.\n", + "\n", + "**Day 3: Leisure and Local Experiences**\n", + "\n", + "**Morning:**\n", + "1. **Natural History Museum**\n", + " - Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.\n", + "\n", + "**Midday:**\n", + "2. **Lunch at South Kensington**\n", + " - Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.\n", + "\n", + "**Afternoon:**\n", + "3. **Hyde Park**\n", + " - Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.\n", + "\n", + "**Evening:**\n", + "4. **Dinner and a Show in the West End**\n", + " - End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.\n", + "\n", + "**Additional Tips:**\n", + "- Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.\n", + "- London’s public transport system is efficient; consider getting an Oyster card for convenient travel.\n", + "- Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: ['Tokyo', 'In Tokyo, the current weather is as follows:\\nDetailed status: few clouds\\nWind speed: 2.57 m/s, direction: 280°\\nHumidity: 92%\\nTemperature: \\n - Current: 10.32°C\\n - High: 10.91°C\\n - Low: 9.19°C\\n - Feels like: 9.81°C\\nRain: {}\\nHeat index: None\\nCloud cover: 20%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g298184-Activities-Tokyo_Tokyo_Prefecture_Kanto.html', 'snippet': 'Things to Do in Tokyo, Japan: See Tripadvisor's 1,602,397 traveler reviews and photos of Tokyo tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Tokyo. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Tokyo - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.lonelyplanet.com/japan/tokyo/attractions', 'snippet': 'Discover the best attractions in Tokyo including Tokyo National Museum, Ghibli Museum, and Golden Gai.', 'title': 'Must-see attractions Tokyo, Japan - Lonely Planet'}, {'link': 'https://www.japan-guide.com/e/e2164.html', 'snippet': 'The city's history can be appreciated in districts such as Asakusa and in many excellent museums, historic temples and gardens. Contrary to common perception, Tokyo also offers a number of attractive green spaces in the city center and within relatively short train rides at its outskirts.', 'title': 'Tokyo City Guide - What to do in Tokyo'}]}]\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: ['Sydney', 'In Sydney, the current weather is as follows:\\nDetailed status: clear sky\\nWind speed: 2.93 m/s, direction: 348°\\nHumidity: 81%\\nTemperature: \\n - Current: 16.95°C\\n - High: 18.04°C\\n - Low: 15.42°C\\n - Feels like: 16.82°C\\nRain: {}\\nHeat index: None\\nCloud cover: 0%', {'result': [{'link': 'https://www.tripadvisor.com/Attractions-g255060-Activities-Sydney_New_South_Wales.html', 'snippet': 'Things to Do in Sydney, Australia: See Tripadvisor's 1,043,552 traveler reviews and photos of Sydney tourist attractions. Find what to do today, this weekend, or in March. We have reviews of the best places to see in Sydney. Visit top-rated & must-see attractions.', 'title': 'THE 15 BEST Things to Do in Sydney - 2024 (with Photos) - Tripadvisor'}, {'link': 'https://www.sydney.com/things-to-do/attractions', 'snippet': 'The UNESCO World Heritage-listed building offers daily guided tours, available in English, Mandarin, French, German, Japanese, Korean and Spanish. Meanwhile, Opera Bar has the best view in town, letting you sip cocktails as you marvel at the Sydney Harbour Bridge.', 'title': 'Top attractions in Sydney | Sydney.com'}, {'link': 'https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck', 'snippet': 'We’ve rounded up 19 infamous Sydney tourist stops that you'll actually enjoy, from thrilling adventures to scenic tours.', 'title': \"19 Amazing Tourist Attractions in Sydney That Don't Suck\"}]}]\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: **Tokyo Itinerary**\n", + "\n", + "**Day 1: Arrival and Exploration of Historical and Cultural Sites**\n", + "\n", + "- **Morning:**\n", + " - **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.\n", + "\n", + "- **Afternoon:**\n", + " - **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.\n", + "\n", + "- **Evening:**\n", + " - **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.\n", + "\n", + "**Day 2: Modern Tokyo and Unique Experiences**\n", + "\n", + "- **Morning:**\n", + " - **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.\n", + "\n", + "- **Afternoon:**\n", + " - **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.\n", + "\n", + "- **Evening:**\n", + " - **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.\n", + "\n", + "**Day 3: Relax and Explore Green Spaces**\n", + "\n", + "- **Morning:**\n", + " - **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.\n", + "\n", + "- **Afternoon:**\n", + " - **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.\n", + "\n", + "- **Evening:**\n", + " - **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.\n", + "\n", + "**Weather Considerations:**\n", + "- With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.\n", + "- Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.\n", + "\n", + "**Additional Tips:**\n", + "- Always check the opening hours of attractions and book tickets in advance where necessary.\n", + "- Use Tokyo’s efficient public transport to move around easily.\n", + "- Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.\n", + "\n", + "### Morning\n", + "\n", + "**9:00 AM - Sydney Opera House**\n", + "- Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.\n", + "- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\n", + "\n", + "**11:00 AM - Royal Botanic Garden Sydney**\n", + "- Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.\n", + "\n", + "### Afternoon\n", + "\n", + "**12:30 PM - Lunch at Opera Bar**\n", + "- Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.\n", + "- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\n", + "\n", + "**2:00 PM - Sydney Harbour Bridge**\n", + "- After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.\n", + "\n", + "**4:00 PM - The Rocks**\n", + "- Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.\n", + "\n", + "### Evening\n", + "\n", + "**6:00 PM - Darling Harbour**\n", + "- Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.\n", + "\n", + "**8:00 PM - Dinner at a Local Restaurant**\n", + "- Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.\n", + "\n", + "### Additional Suggestions\n", + "\n", + "- If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.\n", + "\n", + "With clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: [\"Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions. \\n\\n### Day 1: Iconic Landmarks and Observation Decks\\n- **Morning:**\\n - **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.\\n - **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)\\n\\n- **Afternoon:**\\n - **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.\\n - **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.\\n\\n- **Evening:**\\n - **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.\\n\\n### Day 2: Culture and History\\n- **Morning:**\\n - **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.\\n\\n- **Afternoon:**\\n - **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.\\n\\n- **Evening:**\\n - **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.\\n\\n### Day 3: Historical and Educational\\n- **Morning:**\\n - **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.\\n\\n- **Afternoon:**\\n - **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.\\n\\n- **Evening:**\\n - **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.\\n\\n### Additional Tips:\\n- **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.\\n- **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.\\n- **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.\\n\\nFor more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/). \\n\\nEnjoy your trip to New York City!\", \"**Day 1: Exploring Iconic London Landmarks**\\n\\n**Morning:**\\n1. **Buckingham Palace**\\n - Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.\\n - Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.\\n\\n**Midday:**\\n2. **Westminster Abbey**\\n - Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.\\n\\n3. **Lunch at Borough Market**\\n - Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.\\n\\n**Afternoon:**\\n4. **London Eye**\\n - After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.\\n\\n**Evening:**\\n5. **The Globe Theatre**\\n - As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.\\n\\n**Day 2: Museums and Cultural Exploration**\\n\\n**Morning:**\\n1. **The British Museum**\\n - Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.\\n\\n**Midday:**\\n2. **Lunch near Covent Garden**\\n - Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.\\n\\n**Afternoon:**\\n3. **The Tower of London**\\n - After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.\\n\\n**Evening:**\\n4. **Tower Bridge**\\n - Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.\\n\\n**Day 3: Leisure and Local Experiences**\\n\\n**Morning:**\\n1. **Natural History Museum**\\n - Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.\\n\\n**Midday:**\\n2. **Lunch at South Kensington**\\n - Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.\\n\\n**Afternoon:**\\n3. **Hyde Park**\\n - Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.\\n\\n**Evening:**\\n4. **Dinner and a Show in the West End**\\n - End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.\\n\\n**Additional Tips:**\\n- Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.\\n- London’s public transport system is efficient; consider getting an Oyster card for convenient travel.\\n- Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.\", \"Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:\\n\\n### Day 1: Embrace the Iconic Landmarks\\n\\n**Morning: Eiffel Tower**\\n- **Time:** 9:00 AM\\n- **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.\\n- **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)\\n\\n**Afternoon: Louvre Museum**\\n- **Time:** 1:00 PM\\n- **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.\\n- **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)\\n\\n**Evening: Seine River Cruise**\\n- **Time:** 6:00 PM\\n- **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.\\n\\n### Day 2: Explore Cultural and Historical Treasures\\n\\n**Morning: Notre-Dame Cathedral**\\n- **Time:** 9:30 AM\\n- **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.\\n\\n**Afternoon: Musée d'Orsay**\\n- **Time:** 1:30 PM\\n- **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.\\n\\n**Evening: Montmartre and Sacré-Cœur**\\n- **Time:** 5:00 PM\\n- **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.\\n\\n### Day 3: Discover Hidden Gems and Local Flavors\\n\\n**Morning: Le Marais District**\\n- **Time:** 10:00 AM\\n- **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.\\n\\n**Afternoon: Palais Garnier (Opera House)**\\n- **Time:** 2:00 PM\\n- **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.\\n\\n**Evening: Moulin Rouge Show**\\n- **Time:** 8:00 PM\\n- **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.\\n\\n### Additional Tips:\\n- **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.\\n- **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.\\n- **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.\\n\\nEnjoy your snowy adventure in Paris!\", \"**Tokyo Itinerary**\\n\\n**Day 1: Arrival and Exploration of Historical and Cultural Sites**\\n\\n- **Morning:**\\n - **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.\\n\\n- **Afternoon:**\\n - **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.\\n\\n- **Evening:**\\n - **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.\\n\\n**Day 2: Modern Tokyo and Unique Experiences**\\n\\n- **Morning:**\\n - **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.\\n\\n- **Afternoon:**\\n - **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.\\n\\n- **Evening:**\\n - **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.\\n\\n**Day 3: Relax and Explore Green Spaces**\\n\\n- **Morning:**\\n - **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.\\n\\n- **Afternoon:**\\n - **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.\\n\\n- **Evening:**\\n - **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.\\n\\n**Weather Considerations:**\\n- With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.\\n- Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.\\n\\n**Additional Tips:**\\n- Always check the opening hours of attractions and book tickets in advance where necessary.\\n- Use Tokyo’s efficient public transport to move around easily.\\n- Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.\", \"Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.\\n\\n### Morning\\n\\n**9:00 AM - Sydney Opera House**\\n- Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**11:00 AM - Royal Botanic Garden Sydney**\\n- Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.\\n\\n### Afternoon\\n\\n**12:30 PM - Lunch at Opera Bar**\\n- Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**2:00 PM - Sydney Harbour Bridge**\\n- After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.\\n\\n**4:00 PM - The Rocks**\\n- Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.\\n\\n### Evening\\n\\n**6:00 PM - Darling Harbour**\\n- Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.\\n\\n**8:00 PM - Dinner at a Local Restaurant**\\n- Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.\\n\\n### Additional Suggestions\\n\\n- If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.\\n\\nWith clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!\"]\n", + "--------------------------------------------------\n", + "Transition type: finish\n", + "Transition output: {'final_plan': \"Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions. \\n\\n### Day 1: Iconic Landmarks and Observation Decks\\n- **Morning:**\\n - **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.\\n - **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)\\n\\n- **Afternoon:**\\n - **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.\\n - **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.\\n\\n- **Evening:**\\n - **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.\\n\\n### Day 2: Culture and History\\n- **Morning:**\\n - **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.\\n\\n- **Afternoon:**\\n - **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.\\n\\n- **Evening:**\\n - **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.\\n\\n### Day 3: Historical and Educational\\n- **Morning:**\\n - **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.\\n\\n- **Afternoon:**\\n - **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.\\n\\n- **Evening:**\\n - **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.\\n\\n### Additional Tips:\\n- **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.\\n- **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.\\n- **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.\\n\\nFor more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/). \\n\\nEnjoy your trip to New York City!\\n---------------\\n**Day 1: Exploring Iconic London Landmarks**\\n\\n**Morning:**\\n1. **Buckingham Palace**\\n - Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.\\n - Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.\\n\\n**Midday:**\\n2. **Westminster Abbey**\\n - Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.\\n\\n3. **Lunch at Borough Market**\\n - Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.\\n\\n**Afternoon:**\\n4. **London Eye**\\n - After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.\\n\\n**Evening:**\\n5. **The Globe Theatre**\\n - As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.\\n\\n**Day 2: Museums and Cultural Exploration**\\n\\n**Morning:**\\n1. **The British Museum**\\n - Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.\\n\\n**Midday:**\\n2. **Lunch near Covent Garden**\\n - Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.\\n\\n**Afternoon:**\\n3. **The Tower of London**\\n - After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.\\n\\n**Evening:**\\n4. **Tower Bridge**\\n - Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.\\n\\n**Day 3: Leisure and Local Experiences**\\n\\n**Morning:**\\n1. **Natural History Museum**\\n - Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.\\n\\n**Midday:**\\n2. **Lunch at South Kensington**\\n - Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.\\n\\n**Afternoon:**\\n3. **Hyde Park**\\n - Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.\\n\\n**Evening:**\\n4. **Dinner and a Show in the West End**\\n - End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.\\n\\n**Additional Tips:**\\n- Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.\\n- London’s public transport system is efficient; consider getting an Oyster card for convenient travel.\\n- Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.\\n---------------\\nHere’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:\\n\\n### Day 1: Embrace the Iconic Landmarks\\n\\n**Morning: Eiffel Tower**\\n- **Time:** 9:00 AM\\n- **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.\\n- **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)\\n\\n**Afternoon: Louvre Museum**\\n- **Time:** 1:00 PM\\n- **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.\\n- **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)\\n\\n**Evening: Seine River Cruise**\\n- **Time:** 6:00 PM\\n- **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.\\n\\n### Day 2: Explore Cultural and Historical Treasures\\n\\n**Morning: Notre-Dame Cathedral**\\n- **Time:** 9:30 AM\\n- **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.\\n\\n**Afternoon: Musée d'Orsay**\\n- **Time:** 1:30 PM\\n- **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.\\n\\n**Evening: Montmartre and Sacré-Cœur**\\n- **Time:** 5:00 PM\\n- **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.\\n\\n### Day 3: Discover Hidden Gems and Local Flavors\\n\\n**Morning: Le Marais District**\\n- **Time:** 10:00 AM\\n- **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.\\n\\n**Afternoon: Palais Garnier (Opera House)**\\n- **Time:** 2:00 PM\\n- **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.\\n\\n**Evening: Moulin Rouge Show**\\n- **Time:** 8:00 PM\\n- **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.\\n\\n### Additional Tips:\\n- **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.\\n- **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.\\n- **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.\\n\\nEnjoy your snowy adventure in Paris!\\n---------------\\n**Tokyo Itinerary**\\n\\n**Day 1: Arrival and Exploration of Historical and Cultural Sites**\\n\\n- **Morning:**\\n - **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.\\n\\n- **Afternoon:**\\n - **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.\\n\\n- **Evening:**\\n - **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.\\n\\n**Day 2: Modern Tokyo and Unique Experiences**\\n\\n- **Morning:**\\n - **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.\\n\\n- **Afternoon:**\\n - **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.\\n\\n- **Evening:**\\n - **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.\\n\\n**Day 3: Relax and Explore Green Spaces**\\n\\n- **Morning:**\\n - **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.\\n\\n- **Afternoon:**\\n - **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.\\n\\n- **Evening:**\\n - **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.\\n\\n**Weather Considerations:**\\n- With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.\\n- Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.\\n\\n**Additional Tips:**\\n- Always check the opening hours of attractions and book tickets in advance where necessary.\\n- Use Tokyo’s efficient public transport to move around easily.\\n- Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.\\n---------------\\nHere's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.\\n\\n### Morning\\n\\n**9:00 AM - Sydney Opera House**\\n- Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**11:00 AM - Royal Botanic Garden Sydney**\\n- Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.\\n\\n### Afternoon\\n\\n**12:30 PM - Lunch at Opera Bar**\\n- Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.\\n- **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)\\n\\n**2:00 PM - Sydney Harbour Bridge**\\n- After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.\\n\\n**4:00 PM - The Rocks**\\n- Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.\\n\\n### Evening\\n\\n**6:00 PM - Darling Harbour**\\n- Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.\\n\\n**8:00 PM - Dinner at a Local Restaurant**\\n- Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.\\n\\n### Additional Suggestions\\n\\n- If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.\\n\\nWith clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!\"}\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/03-trip-planning-assistant.py b/cookbooks/03-trip-planning-assistant.py new file mode 100644 index 000000000..e3375a728 --- /dev/null +++ b/cookbooks/03-trip-planning-assistant.py @@ -0,0 +1,161 @@ +import uuid +import yaml +from julep import Client +import os + +openweathermap_api_key = os.getenv("OPENWEATHERMAP_API_KEY") +brave_api_key = os.getenv("BRAVE_API_KEY") + +# Global UUID is generated for agent and task +AGENT_UUID = uuid.uuid4() +TASK_UUID = uuid.uuid4() + +# Creating Julep Client with the API Key +api_key = "" # Your API key here +client = Client(api_key=api_key, environment="dev") + +# Creating an "agent" +name = "Jarvis" +about = "The original AI conscious the Iron Man." +default_settings = { + "temperature": 0.7, + "top_p": 1, + "min_p": 0.01, + "presence_penalty": 0, + "frequency_penalty": 0, + "length_penalty": 1.0, + "max_tokens": 150, +} + +agent = client.agents.create_or_update( + agent_id=AGENT_UUID, + name=name, + about=about, + model="gpt-4o", +) + +# Defining a Task +# Defining the task +task_def = yaml.safe_load(f""" +name: Tourist Plan With Weather And Attractions + +input_schema: + type: object + properties: + locations: + type: array + items: + type: string + description: The locations to search for. + +tools: +- name: wikipedia + type: integration + integration: + provider: wikipedia + +- name: weather + type: integration + integration: + provider: weather + setup: + openweathermap_api_key: {openweathermap_api_key} + +- name: internet_search + type: integration + integration: + provider: brave + setup: + api_key: {brave_api_key} + +main: +- over: inputs[0].locations + map: + tool: weather + arguments: + location: _ + +- over: inputs[0].locations + map: + tool: internet_search + arguments: + query: "'tourist attractions in ' + _" + +# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)] +- evaluate: + zipped: |- + list( + zip( + inputs[0].locations, + [output['result'] for output in outputs[0]], + outputs[1] + ) + ) + + +- over: _['zipped'] + parallelism: 3 + # Inside the map step, each `_` represents the current element in the list + # which is a tuple of (location, weather, attractions) + map: + prompt: + - role: system + content: >- + You are {{{{agent.name}}}}. Your task is to create a detailed itinerary + for visiting tourist attractions in some locations. + The user will give you the following information for each location: + + - The location + - The current weather condition + - The top tourist attractions + - role: user + content: >- + Location: "{{{{_[0]}}}}" + Weather: "{{{{_[1]}}}}" + Attractions: "{{{{_[2]}}}}" + unwrap: true + +- evaluate: + final_plan: |- + '\\n---------------\\n'.join(activity for activity in _) +""") + +# Creating/Updating a task +task = client.tasks.create_or_update( + task_id=TASK_UUID, + agent_id=AGENT_UUID, + **task_def +) + +# Creating an Execution +execution = client.executions.create( + task_id=task.id, + input={ + "locations": ["New York", "London", "Paris", "Tokyo", "Sydney"] + } +) + +print(f"Execution ID: {execution.id}") + +# Wait for the execution to complete +import time +time.sleep(200) + +# Getting the execution details +# Get execution details +execution = client.executions.get(execution.id) +# Print the output +print(execution.output) +print("-"*50) + +if 'final_plan' in execution.output: + print(execution.output['final_plan']) + +# Lists all the task steps that have been executed up to this point in time +transitions = client.executions.transitions.list(execution_id=execution.id).items + +# Transitions are retreived in reverse chronological order +for transition in reversed(transitions): + print("Transition type: ", transition.type) + print("Transition output: ", transition.output) + print("-"*50) \ No newline at end of file diff --git a/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.ipynb b/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.ipynb deleted file mode 100644 index d7fc39840..000000000 --- a/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.ipynb +++ /dev/null @@ -1,420 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "div align=\"center\">\n", - " \"julep\"\n", - "
\n", - "\n", - "## Task: Travel Itinerary Assistant with Weather and Wikipedia Integrations\n", - "\n", - "### Overview\n", - "\n", - "The Travel Itinerary Assistant helps users plan a travel itinerary that takes into account current weather conditions and local tourist attractions. By integrating data from Wikipedia for tourist attractions and using a weather API for real-time weather updates, the tool provides a comprehensive travel plan tailored to each location. The generated itinerary suggests appropriate activities based on the weather, enhancing the overall travel experience.\n", - "\n", - "### Task Flow\n", - "\n", - "1. **User Input**\n", - " - User provides a list of desired travel locations.\n", - " - Each location is processed individually to gather the required data.\n", - "\n", - "2. **Weather Data Retrieval**\n", - " - Fetch current weather data for each location using a weather API.\n", - " - Extract relevant weather details, such as temperature, weather condition, and recommendations.\n", - "\n", - "3. **Tourist Attractions Lookup**\n", - " - Use Wikipedia to search for the top tourist attractions for each location.\n", - " - The query format used is: `\" tourist attractions\"`.\n", - " - Retrieve and compile a list of popular tourist spots and landmarks.\n", - "\n", - "4. **Data Evaluation and Integration**\n", - " - Combine weather data and tourist attractions into a unified list for each location.\n", - " - Format the data into a structured tuple: `(location, weather, attractions)`.\n", - "\n", - "5. **Itinerary Generation**\n", - " - Create a detailed travel itinerary based on:\n", - " - Current weather conditions (e.g., sunny, rainy, cloudy).\n", - " - Top tourist attractions for each location.\n", - " - Suggested activities categorized as indoor or outdoor based on weather.\n", - "\n", - "### Key Features\n", - "\n", - "- **Multi-location Travel Planning**: Handles multiple destinations simultaneously, offering a consolidated travel plan.\n", - "- **Real-time Weather Data**: Leverages weather APIs to provide up-to-date weather conditions.\n", - "- **Tourist Attraction Discovery**: Integrates Wikipedia to find and recommend popular attractions.\n", - "- **Intelligent Itinerary Suggestions**: Suggests indoor or outdoor activities based on the weather.\n", - "- **Comprehensive Itinerary Output**: Combines weather and tourist data into a user-friendly travel plan.\n", - "\n", - "### Output\n", - "\n", - "- A detailed travel itinerary for each location\n", - "- Curated, up-to-date information gathered from weather searches and Wikipedia\n", - "\n", - "```plaintext\n", - "\n", - "+----------------+ +--------------------------+ +--------------------------+ +------------------------------+ +-------------------------+\n", - "| User Input | | Weather Data Retrieval | | Tourist Attractions | | Data Evaluation & Integration| | Itinerary Generation |\n", - "| (List of | --> | (Weather API) | --> | Lookup (Wikipedia) | --> | (Combine Weather & | --> | (Generate Suggested |\n", - "| Locations) | | | | | | Attractions Data) | | Activities/Plan) |\n", - "+----------------+ +--------------------------+ +--------------------------+ +------------------------------+ +-------------------------+\n", - " | | | | |\n", - " | | | | |\n", - " v v v v v\n", - "Location 1, Location 2, ... Fetch weather for each Search Wikipedia for Combine weather data and Create itinerary with\n", - "Each location processed location individually, \" tourist tourist attractions into suggested activities\n", - "individually for extracting temp., attractions\", retrieve a structured tuple: based on weather and\n", - "weather data. conditions, & top spots. (location, weather, attractions.\n", - " recommendations. attractions).\n", - "```\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implementation\n", - "\n", - "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", - "\n", - "\n", - " \"Open\n", - "\n", - "\n", - "### Additional Information\n", - "\n", - "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", - "\n", - "**Author:** Julep AI \n", - "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Installing the Julep Client" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install julep -U --quiet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### NOTE:\n", - "\n", - "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", - "- Once created, these UUIDs should remain unchanged for simplicity.\n", - "- Altering a UUID will result in the system treating it as a new agent or task.\n", - "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Global UUID is generated for agent and task\n", - "import uuid\n", - "\n", - "AGENT_UUID = uuid.uuid4()\n", - "TASK_UUID = uuid.uuid4() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating Julep Client with the API Key" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "from julep import Client\n", - "\n", - "api_key = \"\" # Your API key here\n", - "\n", - "# Create a client\n", - "client = Client(api_key=api_key, environment=\"dev\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an \"agent\"\n", - "\n", - "\n", - "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", - "\n", - "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Defining the agent\n", - "name = \"Jarvis\"\n", - "about = \"The original AI conscious the Iron Man.\"\n", - "default_settings = {\n", - " \"temperature\": 0.7,\n", - " \"top_p\": 1,\n", - " \"min_p\": 0.01,\n", - " \"presence_penalty\": 0,\n", - " \"frequency_penalty\": 0,\n", - " \"length_penalty\": 1.0,\n", - " \"max_tokens\": 150,\n", - "}\n", - "\n", - "# Create the agent\n", - "agent = client.agents.create_or_update(\n", - " agent_id=AGENT_UUID,\n", - " name=name,\n", - " about=about,\n", - " model=\"gpt-4o\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Defining a Task\n", - "\n", - "Tasks in Julep are Github Actions style workflows that define long-running, multi-step actions. \n", - "You can use them to conduct complex actions by defining them step-by-step. They have access to all Julep integrations.\n", - "\n", - "To learn more about tasks, visit [Julep documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#task)." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a Task which uses a agent to generate a sarcastic response to a given text using a DuckDuckGo and Wikipedia tool.\n", - "\n", - "More on how to define a task can be found [here](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md)." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "task_def = yaml.safe_load(\"\"\"\n", - "name: Tourist Plan With Weather And Attractions\n", - "\n", - "input_schema:\n", - " type: object\n", - " properties:\n", - " locations:\n", - " type: array\n", - " items:\n", - " type: string\n", - " description: The locations to search for.\n", - "\n", - "tools:\n", - "- name: wikipedia\n", - " type: integration\n", - " integration:\n", - " provider: wikipedia\n", - "\n", - "- name: weather\n", - " type: integration\n", - " integration:\n", - " provider: weather\n", - " setup:\n", - " openweathermap_api_key: \"YOUR_API_KEY\"\n", - "\n", - "main:\n", - "- over: inputs[0].locations\n", - " map:\n", - " tool: weather\n", - " arguments:\n", - " location: _\n", - "\n", - "- over: inputs[0].locations\n", - " map:\n", - " tool: wikipedia\n", - " arguments:\n", - " query: \"_ + ' tourist attractions'\"\n", - "\n", - "- evaluate:\n", - " zipped: \"list(zip(inputs[0].locations, [output['result'] for output in outputs[0]], [output['documents'][0]['page_content'] for output in outputs[1]]))\" # [(location, weather, attractions)]\n", - "\n", - "\n", - "- over: _['zipped']\n", - " parallelism: 3\n", - " map:\n", - " prompt:\n", - " - role: system\n", - " content: >-\n", - " You are a travel assistant. Your task is to create a detailed itinerary for visiting tourist attractions in \"{{_[0]}}\" based on the weather conditions and the top tourist attractions provided.\n", - " \n", - " Current weather condition at \"{{_[0]}}\":\n", - " \"{{_[1]}}\"\n", - "\n", - " Top tourist attractions in \"{{_[0]}}\":\n", - " \"{{_[2]}}\"\n", - "\n", - " Suggest outdoor or indoor activities based on the above information.\n", - " unwrap: true\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating/Updating a task to generate a sarcastic response to a given text using a Intergation." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# creating the task object\n", - "task = client.tasks.create_or_update(\n", - " task_id=TASK_UUID,\n", - " agent_id=AGENT_UUID,\n", - " **task_def\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an Execution\n", - "\n", - "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creates a execution worflow for the Task defined in the yaml file." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "execution = client.executions.create(\n", - " task_id=task.id,\n", - " input={\n", - " \"locations\": [\"New York\", \"London\", \"Paris\", \"Tokyo\", \"Sydney\"]\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "execution.id" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# getting the execution details\n", - "execution = client.executions.get(execution.id)\n", - "#printing the output\n", - "execution.output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Retrieves and lists all the steps of a defined task that have been executed up to that point in time. Unlike streaming, this function does not continuously monitor the execution; it only provides a snapshot of the steps completed so far without displaying real-time updates as the task progresses." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.list(execution_id=execution.id).items" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Continuously monitor and stream the steps of a defined task. It retrieves and displays the transitions or execution steps of the task identified by execution.id in real-time, showing each step sequentially until the task is either completed or an error causes it to terminate." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "client.executions.transitions.stream(execution_id=execution.id)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "ai", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.py b/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.py deleted file mode 100644 index cde5d71a6..000000000 --- a/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.py +++ /dev/null @@ -1,123 +0,0 @@ -import uuid -import yaml -from julep import Client - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an "agent" -name = "Jarvis" -about = "The original AI conscious the Iron Man." -default_settings = { - "temperature": 0.7, - "top_p": 1, - "min_p": 0.01, - "presence_penalty": 0, - "frequency_penalty": 0, - "length_penalty": 1.0, - "max_tokens": 150, -} - -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name=name, - about=about, - model="gpt-4o", -) - -# Defining a Task -task_def = yaml.safe_load(""" -name: Tourist Plan With Weather And Attractions - -input_schema: - type: object - properties: - locations: - type: array - items: - type: string - description: The locations to search for. - -tools: -- name: wikipedia - type: integration - integration: - provider: wikipedia - -- name: weather - type: integration - integration: - provider: weather - setup: - openweathermap_api_key: "YOUR_API_KEY" - -main: -- over: inputs[0].locations - map: - tool: weather - arguments: - location: _ - -- over: inputs[0].locations - map: - tool: wikipedia - arguments: - query: "_ + ' tourist attractions'" - -- evaluate: - zipped: "list(zip(inputs[0].locations, [output['result'] for output in outputs[0]], [output['documents'][0]['page_content'] for output in outputs[1]]))" # [(location, weather, attractions)] - -- over: _['zipped'] - parallelism: 3 - map: - prompt: - - role: system - content: >- - You are a travel assistant. Your task is to create a detailed itinerary for visiting tourist attractions in "{{_[0]}}" based on the weather conditions and the top tourist attractions provided. - - Current weather condition at "{{_[0]}}": - "{{_[1]}}" - - Top tourist attractions in "{{_[0]}}": - "{{_[2]}}" - - Suggest outdoor or indoor activities based on the above information. - unwrap: true -""") - -# Creating/Updating a task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Creating an Execution -execution = client.executions.create( - task_id=task.id, - input={ - "locations": ["New York", "London", "Paris", "Tokyo", "Sydney"] - } -) - -print(f"Execution ID: {execution.id}") - -# Getting the execution details -execution = client.executions.get(execution.id) -print("Execution Output:") -print(execution.output) - -# List all steps of the executed task -print("Execution Steps:") -for item in client.executions.transitions.list(execution_id=execution.id).items: - print(item) - -# Stream the execution steps in real-time -print("Streaming Execution Steps:") -for step in client.executions.transitions.stream(execution_id=execution.id): - print(step) \ No newline at end of file diff --git a/cookbooks/04-hook-generator-trending-reels.ipynb b/cookbooks/04-hook-generator-trending-reels.ipynb new file mode 100644 index 000000000..7d8dfe6c3 --- /dev/null +++ b/cookbooks/04-hook-generator-trending-reels.ipynb @@ -0,0 +1,1103 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + " \n", + "## Task Definition: Generating Hooks for Trending Instagram Reels\n", + " \n", + "### Overview\n", + " \n", + " This task involves creating engaging hooks for trending Instragram reels using a structured approach that leverages data extracted from various social media APIs. The goal is to enhance the visibility and engagement of these reels through compelling introductory statements.\n", + " \n", + "### Task Tools:\n", + " \n", + "**API Tool Call**: Utilizes API calls to fetch trending data from social media platforms.\n", + " \n", + "**System Tool**: Manages document retrieval that contains templates for generating hooks.\n", + " \n", + "### Task Input:\n", + " \n", + "**query**: The search query or topic for which trending reels need to be analyzed.\n", + " \n", + "### Task Output:\n", + " \n", + "**output**: A JSON object that includes the original reel data along with the newly generated hooks, aimed at maximizing engagement.\n", + " \n", + "#### Task Flow\n", + " \n", + "1. **Input**: The user provides a topic or query.\n", + " \n", + "2. **API Tool Integration**: The `api_tool_call` is used to fetch trending reels based on the input query.\n", + "\n", + "3. **Document Retrieval**: Retrieves a document containing hook templates from the system tool.\n", + "\n", + "4. **Hook Generation**: Utilizes the retrieved templates to generate three unique hooks for each reel.\n", + " \n", + "5. **Output**: The final output includes the original reel data along with its engagement metrics and the generated hooks.\n", + "\n", + "### Key Feature:\n", + "- Utilizes dynamic hook templates and social media trends to generate engaging content for TikTok reels, enhancing viewer interaction and reel visibility.\n", + "\n", + "```plaintext\n", + "+----------+ +------------+ +------------+ +------------+ +-----------+\n", + "| User | | API | | Prompt | | System | | Output |\n", + "| Input | --> | Tool | --> | Step | --> | Tool | --> | Step |\n", + "| (Query) | | Call | | (Scores) | | | | |\n", + "+----------+ +------------+ +------------+ +------------+ +-----------+\n", + " | | | | |\n", + " | | | | |\n", + " v v v v v\n", + " \"trending\" Fetch data Calculate Retrieve hooks \"Here are the\n", + " topics from API engagement & from document trending hooks...\"\n", + " virality scores\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2 μs, sys: 0 ns, total: 2 μs\n", + "Wall time: 4.05 μs\n" + ] + } + ], + "source": [ + "# Global UUID is generated for agent and task\n", + "%time\n", + "import time, yaml\n", + "from dotenv import load_dotenv\n", + "import os\n", + "import uuid\n", + "load_dotenv(override=True)\n", + "\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4()\n", + "RAPID_API_KEY = os.getenv('RAPID_API_KEY')\n", + "RAPID_API_HOST = os.getenv('RAPID_API_HOST')\n", + "JULEP_API_KEY = os.getenv('JULEP_API_KEY') or os.getenv('JULEP_API_KEY_LOCAL')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2 μs, sys: 1 μs, total: 3 μs\n", + "Wall time: 4.05 μs\n", + "AGENT_UUID: 1e0c443f-0e85-4714-989d-87de99a6b4b9\n", + "TASK_UUID: 56ee52db-4d1b-4fd0-8dfc-78d4d58fd6cb\n" + ] + } + ], + "source": [ + "%time\n", + "print(f'AGENT_UUID: {AGENT_UUID}')\n", + "print(f'TASK_UUID: {TASK_UUID}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1 μs, sys: 0 ns, total: 1 μs\n", + "Wall time: 2.86 μs\n" + ] + } + ], + "source": [ + "%time\n", + "from julep import Client\n", + "# # Create a client\n", + "client = Client(api_key=JULEP_API_KEY,environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating an \"agent\"\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 3 μs, sys: 63 μs, total: 66 μs\n", + "Wall time: 67.9 μs\n" + ] + } + ], + "source": [ + "%time\n", + "# Creating an agent for handling persistent sessions\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=\"Session Manager\",\n", + " about=\"An AI agent specialized in managing persistent sessions and context.\",\n", + " model=\"gpt-4o\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating a Document for Hooks" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:145: SyntaxWarning: invalid escape sequence '\\%'\n", + "<>:145: SyntaxWarning: invalid escape sequence '\\%'\n", + "/var/folders/6z/8ysh_n1x55v0l9ljnsyvvffr0000gn/T/ipykernel_1616/2293527858.py:145: SyntaxWarning: invalid escape sequence '\\%'\n", + " \"99\\% of your [target audience] don't. To be the 1% you need to [X].\",\n" + ] + }, + { + "data": { + "text/plain": [ + "'9fcc4c9d-9439-48f0-ac8c-c5ed67dc7c70'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hooks_data = [\n", + " {\"categories\": \"Price-Focused\",\n", + " \"content\": [\n", + " \"Save both time and money with [product].\",\n", + " \"Save both time and money on [task].\",\n", + " \"Is [product] worth the price? Let’s find out!\",\n", + " \"Why is it challenging and costly to find a good [product category]?\",\n", + " \"Searching for an affordable [product type]? Check this out!\",\n", + " \"How to locate an affordable [service].\",\n", + " \"I can’t believe this costs only [price].\",\n", + " \"Don't waste your money on [X]; instead buy [X].\"\n", + " ]},\n", + " {\"categories\": \"Informative\",\n", + " \"content\": [\n", + " \"What’s inside [product]?\",\n", + " \"Common questions I get about [product].\",\n", + " \"Now you can get X delivered right to your door.\",\n", + " \"Thinking of trying [product type]?\",\n", + " \"Allow me to introduce you to [product].\",\n", + " \"[category] Tip #X.\",\n", + " \"Here’s how to achieve [value prop].\",\n", + " \"I know this sounds unbelievable, but…\",\n", + " \"How to get X done in just 10 minutes.\",\n", + " \"How to find time for X.\",\n", + " \"Here’s my biggest life hack for X.\",\n", + " \"Add more [value prop] to your day.\"\n", + " ]},\n", + " {\"categories\": \"Versus the Alternative (or Competition)\",\n", + " \"content\": [\"Before you try [product type], watch this first.\",\n", + " \"Hate [the worse alternative]? Give this a try!\",\n", + " \"Thinking about [worse alternative]?\",\n", + " \"Instead of [worse alternative], try this.\",\n", + " \"Still using [the worse alternative to the product]? Watch this.\",\n", + " \"Something that’s always annoyed me about X.\",\n", + " \"Don’t buy X that doesn’t work. Try this instead.\",\n", + " \"I tested every [product category] so you don’t have to: here’s what I found.\",\n", + " \"Stop doing [worse alternative]. Try [product] instead.\",\n", + " \"Dealing with [negative experience]? I used [product] to help.\",\n", + " \"Why millennials are switching to [product].\",\n", + " \"How to do X without [worse alternative].\",\n", + " \"I only get my [product category] from [brand name].\",\n", + " \"I no longer buy my [product category] from [worse alternative].\",\n", + " \"[Worse alternative] can be difficult to deal with.\",\n", + " \"Your new X alternative.\",\n", + " \"[value prop] without the [negative side effect].\",\n", + " \"I kept experiencing [pain point], so I tried this instead!\",\n", + " \"If you have [pain point] — you need to see this!\",\n", + " \"I wanted to stop doing X, so I tried this instead.\",\n", + " \"Your X isn't [adverb]; you just need X.\",\n", + " \"My secret to [popular trend] revealed!\"\n", + " ]},\n", + " {\"categories\": \"User Experience\",\n", + " \"content\": [\n", + " \"Guys, it’s here…\",\n", + " \"What I ordered vs. what I received.\",\n", + " \"[Product] unboxing.\",\n", + " \"Let’s create X with [product].\",\n", + " \"POV: You tried [product].\",\n", + " \"A day in the life of X.\",\n", + " \"Get ready with me to do [task].\",\n", + " \"“Put a finger down” [product category] edition.\",\n", + " \"Trying home remedies for X.\",\n", + " \"[Product category] ASMR.\"\n", + " ]},\n", + " {\"categories\": \"Responding to Hype\",\n", + " \"content\": [\n", + " \"TikTok made me try [product].\",\n", + " \"Things TikTok made me try #13.\",\n", + " \"This [product type] is going viral on [social media platform].\",\n", + " \"I tested the viral [product type] to see if it lives up to the hype.\",\n", + " \"This [product type] has over 5,000 reviews… let’s see if it’s worth it.\",\n", + " \"[Publication] can’t stop raving about us.\",\n", + " \"It’s so good it sold out in a week.\"\n", + " ]},\n", + " {\"categories\": \"It’s Easier\",\n", + " \"content\": [\n", + " \"Are you [accomplishing the goal optimally]?\",\n", + " \"Life Hack: Try [product] for [pain point].\",\n", + " \"My go-to [product] for [pain point].\",\n", + " \"How to easily [task].\",\n", + " \"[Task] has never been easier than with [product].\",\n", + " \"My favorite [product] to make [hard task] simpler.\",\n", + " \"Here’s my top product for [task].\",\n", + " \"Struggling to do [task]?\",\n", + " \"I’ve been struggling with [task], but [product] has really helped.\",\n", + " \"Easiest way to do [task]?\",\n", + " \"Make your week easier.\",\n", + " \"Why adults avoid [task]… [product] makes it easy.\",\n", + " \"[Product] made [task] so much easier! You’ve got to try it.\",\n", + " \"When I use [product], it’s one less thing I have to worry about.\",\n", + " \"How to do [X] in half the time.\",\n", + " \"This trick/hack/method will save you hours...\",\n", + " \"Easy hack to [X]...\",\n", + " \"Simple [X] that will make you [X].\"\n", + " ]},\n", + " {\"categories\": \"Lists\",\n", + " \"content\": [\n", + " \"5 Ways [product] Helps with [pain point].\",\n", + " \"3 reasons to buy [product].\",\n", + " \"3 reasons to try [service].\",\n", + " \"Get [value prop] in 3 steps.\",\n", + " \"Here are 3 ways [worse alternative] affects your life.\",\n", + " \"5 things you didn’t know about [topic].\",\n", + " \"The ultimate [X] checklist to [action].\",\n", + " \"Reasons why [X].\",\n", + " \"Here are the 3 best ways to [X].\",\n", + " \"Here are [X] mistakes you might be making...\",\n", + " \"If you want [X], do these 5 things...\"\n", + " ]},\n", + " {\"categories\": \"The Best\",\n", + " \"content\": [\n", + " \"The internet’s #1 [product type].\",\n", + " \"The best way to [accomplish the goal of the product].\",\n", + " \"What makes [the product type] the best?\",\n", + " \"My skin has never looked better with [product].\",\n", + " \"The best way to find X in 2022.\",\n", + " \"[Product] changed how I do [task], and I’m never going back.\",\n", + " \"Why is [product] so good though?\",\n", + " \"After hours of researching, I found the best [product type] for [task].\",\n", + " \"I found the best [product category] for [value prop].\",\n", + " \"This is going to blow your mind.\",\n", + " \"How I got [X] in 24 hours.\",\n", + " \"Must-have [products] for [X].\",\n", + " \"The best [target audience] know something that you don't.\"\n", + " ]},\n", + " {\"categories\": \"Other Video Hooks that Address Viewers Directly\",\n", + " \"content\": [\n", + " \"Hey, [customer type], you’ve got to try this.\",\n", + " \"People looking for [product category], stop scrolling.\",\n", + " \"Wait, have you tried X?\",\n", + " \"Take control of your X with [product].\",\n", + " \"Imagine if X was also X.\",\n", + " \"Watch this if you X.\"\n", + " ]},\n", + " {\"categories\": \"Facts & Stats\",\n", + " \"content\": [\n", + " \"PSA: [statement about product category].\",\n", + " \"Did you know? [fact about product category].\",\n", + " \"I just found out [fact about product category].\",\n", + " \"Are you one of [fact about product category] people who do X?\",\n", + " \"New customers get [discount].\",\n", + " \"Take [discount] off when you try [product].\",\n", + " \"I didn’t know X could be related to X.\",\n", + " \"Why is it important to [do product-related task]?\",\n", + " \"99\\% of your [target audience] don't. To be the 1% you need to [X].\",\n", + " \"This [product] is the secret to [X].\"\n", + " ]},\n", + " {\"categories\": \"Curiosity & Engagement\",\n", + " \"content\": [\n", + " \"Is there anything worse than [X]?\",\n", + " \"I will never [adjective] from learning this.\",\n", + " \"X people start scrolling. I have the perfect [X] for you.\",\n", + " \"Here's a challenge for you...\",\n", + " \"There's nothing more painful than [X].\",\n", + " \"What would you do if...\",\n", + " \"Watch till the end…\"\n", + " ]},\n", + " {\"categories\": \"Negative Hooks\",\n", + " \"content\": [\n", + " \"Why you're failing at [activity].\",\n", + " \"The worst mistake you can make in [subject].\",\n", + " \"Avoid these common pitfalls in [topic].\",\n", + " \"The dark side of [popular trend].\",\n", + " \"Don't be fooled by [misconception].\",\n", + " \"Why [common practice] is ruining your [outcome].\",\n", + " \"What no one tells you about [issue].\",\n", + " \"The hidden dangers of [activity].\",\n", + " \"Stop doing this if you want to succeed in [field].\",\n", + " \"The ugly truth about [common belief].\",\n", + " \"Why [habit] is wasting your time.\",\n", + " \"Exposing the myths about [topic].\",\n", + " \"The biggest regret you'll have in [situation].\",\n", + " \"How [action] is destroying your [goal].\",\n", + " \"The shocking reality of [popular topic].\",\n", + " \"Why [trend] is a bad idea.\",\n", + " \"The real reason you're not seeing results in [field].\",\n", + " \"The downside of [seemingly positive aspect].\",\n", + " \"What you should never do in [activity].\",\n", + " \"Why [common advice] is actually harmful.\"\n", + " ]}\n", + "]\n", + "\n", + "hooks_doc_content = []\n", + "\n", + "for category in hooks_data:\n", + " hooks_doc_content.extend(category['content'])\n", + "\n", + "doc = client.agents.docs.create(\n", + " agent_id=AGENT_UUID, title=\"hooks_doc\", content=hooks_doc_content)\n", + "\n", + "doc.id" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Listing the Documents" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Save both time and money with [product].', 'Save both time and money on [task].', 'Is [product] worth the price? Let’s find out!', 'Why is it challenging and costly to find a good [product category]?', 'Searching for an affordable [product type]? Check this out!', 'How to locate an affordable [service].', 'I can’t believe this costs only [price].', \"Don't waste your money on [X]; instead buy [X].\", 'What’s inside [product]?', 'Common questions I get about [product].', 'Now you can get X delivered right to your door.', 'Thinking of trying [product type]?', 'Allow me to introduce you to [product].', '[category] Tip #X.', 'Here’s how to achieve [value prop].', 'I know this sounds unbelievable, but…', 'How to get X done in just 10 minutes.', 'How to find time for X.', 'Here’s my biggest life hack for X.', 'Add more [value prop] to your day.', 'Before you try [product type], watch this first.', 'Hate [the worse alternative]? Give this a try!', 'Thinking about [worse alternative]?', 'Instead of [worse alternative], try this.', 'Still using [the worse alternative to the product]? Watch this.', 'Something that’s always annoyed me about X.', 'Don’t buy X that doesn’t work. Try this instead.', 'I tested every [product category] so you don’t have to: here’s what I found.', 'Stop doing [worse alternative]. Try [product] instead.', 'Dealing with [negative experience]? I used [product] to help.', 'Why millennials are switching to [product].', 'How to do X without [worse alternative].', 'I only get my [product category] from [brand name].', 'I no longer buy my [product category] from [worse alternative].', '[Worse alternative] can be difficult to deal with.', 'Your new X alternative.', '[value prop] without the [negative side effect].', 'I kept experiencing [pain point], so I tried this instead!', 'If you have [pain point] — you need to see this!', 'I wanted to stop doing X, so I tried this instead.', \"Your X isn't [adverb]; you just need X.\", 'My secret to [popular trend] revealed!', 'Guys, it’s here…', 'What I ordered vs. what I received.', '[Product] unboxing.', 'Let’s create X with [product].', 'POV: You tried [product].', 'A day in the life of X.', 'Get ready with me to do [task].', '“Put a finger down” [product category] edition.', 'Trying home remedies for X.', '[Product category] ASMR.', 'TikTok made me try [product].', 'Things TikTok made me try #13.', 'This [product type] is going viral on [social media platform].', 'I tested the viral [product type] to see if it lives up to the hype.', 'This [product type] has over 5,000 reviews… let’s see if it’s worth it.', '[Publication] can’t stop raving about us.', 'It’s so good it sold out in a week.', 'Are you [accomplishing the goal optimally]?', 'Life Hack: Try [product] for [pain point].', 'My go-to [product] for [pain point].', 'How to easily [task].', '[Task] has never been easier than with [product].', 'My favorite [product] to make [hard task] simpler.', 'Here’s my top product for [task].', 'Struggling to do [task]?', 'I’ve been struggling with [task], but [product] has really helped.', 'Easiest way to do [task]?', 'Make your week easier.', 'Why adults avoid [task]… [product] makes it easy.', '[Product] made [task] so much easier! You’ve got to try it.', 'When I use [product], it’s one less thing I have to worry about.', 'How to do [X] in half the time.', 'This trick/hack/method will save you hours...', 'Easy hack to [X]...', 'Simple [X] that will make you [X].', '5 Ways [product] Helps with [pain point].', '3 reasons to buy [product].', '3 reasons to try [service].', 'Get [value prop] in 3 steps.', 'Here are 3 ways [worse alternative] affects your life.', '5 things you didn’t know about [topic].', 'The ultimate [X] checklist to [action].', 'Reasons why [X].', 'Here are the 3 best ways to [X].', 'Here are [X] mistakes you might be making...', 'If you want [X], do these 5 things...', 'The internet’s #1 [product type].', 'The best way to [accomplish the goal of the product].', 'What makes [the product type] the best?', 'My skin has never looked better with [product].', 'The best way to find X in 2022.', '[Product] changed how I do [task], and I’m never going back.', 'Why is [product] so good though?', 'After hours of researching, I found the best [product type] for [task].', 'I found the best [product category] for [value prop].', 'This is going to blow your mind.', 'How I got [X] in 24 hours.', 'Must-have [products] for [X].', \"The best [target audience] know something that you don't.\", 'Hey, [customer type], you’ve got to try this.', 'People looking for [product category], stop scrolling.', 'Wait, have you tried X?', 'Take control of your X with [product].', 'Imagine if X was also X.', 'Watch this if you X.', 'PSA: [statement about product category].', 'Did you know? [fact about product category].', 'I just found out [fact about product category].', 'Are you one of [fact about product category] people who do X?', 'New customers get [discount].', 'Take [discount] off when you try [product].', 'I didn’t know X could be related to X.', 'Why is it important to [do product-related task]?', \"99\\\\% of your [target audience] don't. To be the 1% you need to [X].\", 'This [product] is the secret to [X].', 'Is there anything worse than [X]?', 'I will never [adjective] from learning this.', 'X people start scrolling. I have the perfect [X] for you.', \"Here's a challenge for you...\", \"There's nothing more painful than [X].\", 'What would you do if...', 'Watch till the end…', \"Why you're failing at [activity].\", 'The worst mistake you can make in [subject].', 'Avoid these common pitfalls in [topic].', 'The dark side of [popular trend].', \"Don't be fooled by [misconception].\", 'Why [common practice] is ruining your [outcome].', 'What no one tells you about [issue].', 'The hidden dangers of [activity].', 'Stop doing this if you want to succeed in [field].', 'The ugly truth about [common belief].', 'Why [habit] is wasting your time.', 'Exposing the myths about [topic].', \"The biggest regret you'll have in [situation].\", 'How [action] is destroying your [goal].', 'The shocking reality of [popular topic].', 'Why [trend] is a bad idea.', \"The real reason you're not seeing results in [field].\", 'The downside of [seemingly positive aspect].', 'What you should never do in [activity].', 'Why [common advice] is actually harmful.']\n" + ] + } + ], + "source": [ + "for doc in client.agents.docs.list(agent_id=AGENT_UUID):\n", + " print(doc.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Defining a Task\n", + "import yaml\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Trending Reels Hook Generator\n", + "\n", + "tools:\n", + "- name: api_tool_call\n", + " type: api_call\n", + " api_call:\n", + " method: GET\n", + " url: \"https://instagram-scraper-api3.p.rapidapi.com/reels_by_keyword\"\n", + " headers:\n", + " x-rapidapi-key: \"{RAPID_API_KEY}\"\n", + " x-rapidapi-host: \"{RAPID_API_HOST}\"\n", + " follow_redirects: true\n", + "\n", + "- name: get_hooks_doc\n", + " type: system\n", + " system:\n", + " resource: agent\n", + " subresource: doc\n", + " operation: list\n", + "\n", + "main:\n", + "\n", + "- tool: api_tool_call\n", + " arguments:\n", + " params:\n", + " query: \"inputs[0].topic\"\n", + "\n", + "- evaluate:\n", + " summary: \"list({{\n", + " 'caption': ((clip.get('media') or {{}}).get('caption') or {{}}).get('text') or 'No Caption Available',\n", + " 'code': (clip.get('media') or {{}}).get('code') or 'No Code Available',\n", + " 'media_id': ((clip.get('media') or {{}}).get('caption') or {{}}).get('media_id', 0),\n", + " 'video_duration': (clip.get('media') or {{}}).get('video_duration', 0),\n", + " 'thumbnail_url': (clip.get('media') or {{}}).get('image_versions2', {{}}).get('candidates', [{{}}])[0].get('url') or 'No Thumbnail URL Available',\n", + " 'video_url_360': [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 360][0] if [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 360] else 'No URL Available',\n", + " 'video_url_720': [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 720][0] if [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 720] else 'No URL Available',\n", + " 'play_count': (clip.get('media') or {{}}).get('play_count', 0),\n", + " 'like_count': (clip.get('media') or {{}}).get('like_count', 0),\n", + " 'comment_count': (clip.get('media') or {{}}).get('comment_count', 0),\n", + " 'reshare_count': (clip.get('media') or {{}}).get('reshare_count', 0),\n", + " 'has_audio': (clip.get('media') or {{}}).get('has_audio', False),\n", + " 'audio_type': (clip.get('media') or {{}}).get('clips_metadata', {{}}).get('audio_type', 'No Audio Type Available'),\n", + " 'username': (clip.get('media') or {{}}).get('user', {{}}).get('username') or 'No Username Available',\n", + " 'full_name': (clip.get('media') or {{}}).get('user', {{}}).get('full_name') or 'No Full Name Available',\n", + " 'profile_pic_url': (clip.get('media') or {{}}).get('user', {{}}).get('profile_pic_url') or 'No Profile Pic URL Available',\n", + " 'virality_score': (clip.get('media') or {{}}).get('reshare_count', 0) / ((clip.get('media') or {{}}).get('play_count', 1) if (clip.get('media') or {{}}).get('play_count', 0) > 0 else 1),\n", + " 'engagement_score': (clip.get('media') or {{}}).get('like_count', 0) / ((clip.get('media') or {{}}).get('play_count', 1) if (clip.get('media') or {{}}).get('play_count', 0) > 0 else 1)\n", + " }} for clip in _['json']['data']['reels_serp_modules'][0]['clips'])\"\n", + "\n", + "- over: _['summary']\n", + " parallelism: 4\n", + " map:\n", + " prompt:\n", + " - role: system\n", + " content: >-\n", + " You are a skilled agent tasked with creating a single description for each trending real estate reel for the given topic: {{{{inputs[0].topic}}}}.\n", + " Use information gathered from the following data sources to gather the most relevant information:\n", + "\n", + " Search Results: {{{{_['caption']}}}}\n", + " Virality Score: {{{{_['virality_score']}}}}\n", + " Engagement Score: {{{{_['engagement_score']}}}}\n", + "\n", + " Provide a json repsonse containing the caption, virality score, enagement score and one-liner description for the reel.\n", + " unwrap: true \n", + "\n", + "- evaluate:\n", + " summary: outputs[1]['summary']\n", + " description: list(load_json(res.replace(\"```json\", \"\").replace(\"```\", \"\")) for res in _)\n", + "\n", + "- tool: get_hooks_doc\n", + " arguments:\n", + " agent_id: \"'{AGENT_UUID}'\"\n", + " \n", + "- evaluate:\n", + " hooks_doc: _[0]['content']\n", + "\n", + "- over: outputs[3]['description']\n", + " parallelism: 4\n", + " map:\n", + " prompt:\n", + " - role: system\n", + " content: >-\n", + " You are a skilled content creator tasked with generating 3 engaging video hooks for each reel having its description and caption. Use the following document containing hook templates to create effective hooks:\n", + " \n", + " {{{{_.hooks_doc}}}}\n", + " \n", + " Here are the caption and description to create hooks for:\n", + "\n", + " Caption: {{{{_['caption']}}}}\n", + " Description: {{{{_['description']}}}}\n", + " Virality Score: {{{{_['virality_score']}}}}\n", + " Engagement Score: {{{{_['engagement_score']}}}}\n", + "\n", + " Your task is to generate 3 hooks (for the reel) by adapting the most suitable templates from the document. Each hook should be no more than 1 sentence long and directly relate to its corresponding idea.\n", + " \n", + " Basically, all the ideas are taken from a search about this topic, which is {{{{inputs[0].topic}}}}. You should focus on this while writing the hooks.\n", + "\n", + " Ensure that each hook is creative, engaging, and relevant to its idea while following the structure of the chosen template.\n", + "\n", + " Provide a json repsonse containing the caption, virality score, enagement score, description and the list of 3 hooks for the reel.\n", + " unwrap: true\n", + "\n", + "- evaluate:\n", + " summary: outputs[3]['summary']\n", + " hooks: list(load_json(res.replace(\"```json\", \"\").replace(\"```\", \"\")) for res in _)\n", + "\"\"\")\n", + "\n", + "# Creating/Updating a task\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating an Execution\n", + "\n", + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2 μs, sys: 22 μs, total: 24 μs\n", + "Wall time: 25.3 μs\n" + ] + } + ], + "source": [ + "%time\n", + "# Creating an execution of the task\n", + "execution = client.executions.create(\n", + " task_id=task.id,\n", + " input={\n", + " \"topic\": \"halloween snacks\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"hooks\": [\n", + " {\n", + " \"caption\": \"Halloween puff pastries \\ud83e\\udd87\\ud83d\\udc7b\",\n", + " \"description\": \"Spookify your snack game with these creative Halloween-themed puff pastries, perfect for a festive treat!\",\n", + " \"engagement_score\": 0.029510912856460098,\n", + " \"hooks\": [\n", + " \"Ready to transform your kitchen into a haunted bakery with these spooky puff pastries?\",\n", + " \"Discover the secret to crafting the ultimate Halloween puff pastry that will wow your guests!\",\n", + " \"Watch as these ordinary pastries turn into Halloween masterpieces with a few spooky twists!\"\n", + " ],\n", + " \"virality_score\": 0.0025553608411661623\n", + " },\n", + " {\n", + " \"caption\": \"So good \\ud83e\\udd29\\ud83c\\udf4e\",\n", + " \"description\": \"Discover a delicious and easy Halloween snack idea with this fun reel by createdbyjoyy on TikTok. Perfect for adding a spooky touch to your seasonal treats!\",\n", + " \"engagement_score\": 0.00012093848262517131,\n", + " \"hooks\": [\n", + " \"Unleash the spooky spirit with this simple Halloween snack hack! \\ud83c\\udf83\\ud83c\\udf4f\",\n", + " \"Transform ordinary apples into a spooky Halloween delight! \\ud83d\\udc7b\\ud83c\\udf4e\",\n", + " \"Bite into Halloween magic with these frightfully fun apple snacks! \\ud83e\\uddd9\\u200d\\u2640\\ufe0f\\ud83c\\udf4e\"\n", + " ],\n", + " \"virality_score\": 0.0\n", + " },\n", + " {\n", + " \"caption\": \"2 Weeks until Halloween \\ud83c\\udf83 \\u2022 \\u2022 \\u2022 #restock #halloween #snacks #candy #chocolate #snackbox #spookyseason #spooky #spookytreats #halloweenvibes #asmr #asmrsounds #satisfying #satisfyingvideos\",\n", + " \"description\": \"Prepare for the ultimate spooky season with a restock of delicious Halloween snacks that promise satisfying ASMR sounds and vibes!\",\n", + " \"enagement_score\": 0.055994998851777195,\n", + " \"hooks\": [\n", + " \"\\ud83c\\udf83 What's inside the ultimate Halloween snack box? Unbox the spookiness now! \\ud83c\\udf6c\\ud83d\\udc7b\",\n", + " \"\\ud83d\\udc7b Discover the ASMR magic hidden within these Halloween treats! \\ud83c\\udfb6\\ud83c\\udf6b\",\n", + " \"\\ud83c\\udf6c Trick or treat yourself to satisfying snack sounds this spooky season! \\ud83c\\udf83\\ud83d\\udd0a\"\n", + " ],\n", + " \"virality_score\": 0.0016457860223010384\n", + " },\n", + " {\n", + " \"caption\": \"Is it too early for Halloween content?\\ud83e\\udd2d\\ud83c\\udf83 I love making spooky/cute foods, it\\u2019s so much fun\\ud83d\\udc7b which one of these is your favorite?\\u2063\\u2063 Paired my snack with a Beetlejuice themed @fanta_ch \\ud83c\\udf79have you seen the movie Beetlejuice Beetlejuice yet?\\ud83e\\udd70 #ad #BeetlejuiceXFanta #FantaSwitzerland #FANTAsticHalloween \\u2063#Beetlejuice#Beetlejuice #breakfastideas #breakfast #veganbreakfast #veganbreakfastideas #halloween #halloweenfood #spooky #spookyseason #spookyfood #toasttuesday #toastideas #vegantoast #veganmealideas #cutefood #halloweensnacks #swissvegan #veganschweiz\",\n", + " \"description\": \"Virality Score: 0.005896868607355986 Enagement Score: 0.0464307329578981\",\n", + " \"enagement_score\": 0.0464307329578981,\n", + " \"hooks\": [\n", + " \"Ever wondered how to make your breakfast spooktacular this Halloween? \\ud83c\\udf83\\ud83d\\udc7b\",\n", + " \"Transform your toast into a Halloween masterpiece with these cute and spooky ideas! \\ud83c\\udf5e\\ud83d\\udd78\\ufe0f\",\n", + " \"Is it too early for a taste of Halloween magic? Discover my favorite spooky snacks! \\ud83d\\udc7b\\ud83c\\udf83\"\n", + " ],\n", + " \"virality_score\": 0.005896868607355986\n", + " },\n", + " {\n", + " \"caption\": \"So good \\ud83e\\udd29\\ud83c\\udf4e\",\n", + " \"description\": \"Discover deliciously spooky Halloween snacks with a fresh apple twist that's sure to impress!\",\n", + " \"engagement_score\": 0.06375733232509502,\n", + " \"hooks\": [\n", + " \"Get ready to bite into spooky goodness with these apple-inspired Halloween treats!\",\n", + " \"Transform your Halloween snack game with these delightfully eerie apple creations!\",\n", + " \"Unleash the magic of fresh apples for a spine-chilling snack experience this Halloween!\"\n", + " ],\n", + " \"virality_score\": 0.011431567434847216\n", + " },\n", + " {\n", + " \"caption\": \"The only spider-related thing I can support are these spooky movie night snackies \\ud83e\\udde1\\ud83c\\udf83\",\n", + " \"description\": \"Dive into Halloween fun with these creative spider-themed snacks perfect for a spooky movie night!\",\n", + " \"enagement_score\": 0.05603136931173253,\n", + " \"hooks\": [\n", + " \"Ready to spin a web of deliciousness for your Halloween movie night?\",\n", + " \"These spider snacks are creeping their way into your spooky movie plans!\",\n", + " \"Who knew spiders could be this tasty and fun for a movie night?\"\n", + " ],\n", + " \"virality_score\": 0.011041172221648953\n", + " },\n", + " {\n", + " \"caption\": \"HOW TO MAKE A SPOOKY FRUIT PLATTER\\ud83d\\udc7b\\ud83c\\udf83\\ud83d\\udd77\\ufe0f\\ud83d\\udc41\\ufe0f\",\n", + " \"description\": \"Create a fun and spooky fruit platter with apple monsters, cutie pumpkins, grape eyeballs, kiwi Frankensteins, and more for a healthy Halloween treat!\",\n", + " \"enagement_score\": 0.027146213422569777,\n", + " \"hooks\": [\n", + " \"Transform your fruit into frightful delights this Halloween!\",\n", + " \"Ever seen a kiwi Frankenstein? Make your Halloween platter spooktacular!\",\n", + " \"Turn ordinary apples into monstrous munchies for a healthy Halloween!\"\n", + " ],\n", + " \"virality_score\": 0.02452208417889396\n", + " },\n", + " {\n", + " \"caption\": \"Snack ideas for a halloween party! \\ud83d\\udc7b\\ud83c\\udf6b these were all super easy to put together and perfect for a spooky little halloween party!\",\n", + " \"description\": \"Whip up some easy and spooky Halloween treats like chocolate bats, spiderweb cupcakes, spider donuts, and a Halloween-themed cake to delight your party guests!\",\n", + " \"enagement_score\": 0.05757229777578657,\n", + " \"hooks\": [\n", + " \"Transform your Halloween table with these spooky chocolate bats!\",\n", + " \"Create web-tastic spiderweb cupcakes that are sure to wow your guests!\",\n", + " \"Serve up these creepy-cool spider donuts for a frightfully fun party treat!\"\n", + " ],\n", + " \"virality_score\": 0.010397005477432688\n", + " },\n", + " {\n", + " \"caption\": \"Spooky snack @pillsbury #snack #toddlersnacks #bakewithme #halloweensnacks #toddlermom #halloweenfood\",\n", + " \"description\": \"Discover fun and easy Halloween snack ideas perfect for toddlers, featuring spooky treats with Pillsbury.\",\n", + " \"enagement_score\": 0.030355659215807965,\n", + " \"hooks\": [\n", + " \"Transform your toddler's snack time with these spooktacular Pillsbury Halloween treats!\",\n", + " \"Unleash the magic of Halloween with these easy Pillsbury snacks your toddler will love!\",\n", + " \"Ready to bake up some frightfully fun snacks? Join us for a Pillsbury Halloween treat adventure!\"\n", + " ],\n", + " \"virality_score\": 0.000980990800708716\n", + " },\n", + " {\n", + " \"caption\": \"#SponsoredByMarsWrigley \\ud83c\\udf83 Monster Mash Snack Cups \\ud83c\\udf83\\u2063\\u2063\",\n", + " \"description\": \"Create a festive Halloween popcorn board with spooky M&M\\u2019S\\u00ae flavors like Pumpkin Pie and Peanut Butter Ghoul\\u2019s Blend, perfect for movie night snacking!\",\n", + " \"engagement_score\": 0.034613103498648747,\n", + " \"hooks\": [\n", + " \"\\ud83c\\udfac Ready for a spooktacular movie night with Halloween M&M'S\\u00ae treats? \\ud83c\\udf7f\",\n", + " \"\\ud83d\\udc7b Transform your popcorn board into a Halloween masterpiece with M&M\\u2019S\\u00ae Ghoul\\u2019s Blends! \\ud83c\\udf83\",\n", + " \"\\ud83c\\udf83 Discover the ultimate Halloween snack combo with M&M\\u2019S\\u00ae spooky flavors! \\ud83c\\udf6c\"\n", + " ],\n", + " \"virality_score\": 0.015832298590314806\n", + " },\n", + " {\n", + " \"caption\": \"Back with Episode 2 of Halloween with Pretzels! \\ud83c\\udf83\\ud83d\\udc7b\\ud83e\\udd68\",\n", + " \"description\": \"Spice up your Halloween with 5 spooky pretzel treats that are easy to make and perfect for some fun with the kids!\",\n", + " \"enagement_score\": 0.09330406147091108,\n", + " \"hooks\": [\n", + " \"Discover the 5 spook-tacular pretzel treats that will haunt your snack table this Halloween!\",\n", + " \"Ready to conjure up some Halloween magic with these frightfully fun pretzel creations?\",\n", + " \"Turn your Halloween into a deliciously spooky affair with these easy pretzel tricks!\"\n", + " ],\n", + " \"virality_score\": 0.013995609220636664\n", + " },\n", + " {\n", + " \"caption\": \"FRANKENSTEIN GUACAMOLE HEAD! \\ud83d\\udda4\\ud83d\\udc9a this was so easy and so fun to serve at your Halloween party! #halloweensnacks\",\n", + " \"description\": \"Turn your Halloween party into a monstrous hit with this simple and fun Frankenstein Guacamole Head!\",\n", + " \"engagement_score\": 0.023059542622800945,\n", + " \"hooks\": [\n", + " \"Transform your Halloween table with a frightfully delicious Frankenstein Guacamole Head!\",\n", + " \"Looking for a spooky snack that's a scream at parties? Try the Frankenstein Guacamole Head!\",\n", + " \"Unleash the monster of all party snacks with this easy Frankenstein Guacamole Head!\"\n", + " ],\n", + " \"virality_score\": 0.030018154035028145\n", + " }\n", + " ],\n", + " \"summary\": [\n", + " {\n", + " \"audio_type\": \"licensed_music\",\n", + " \"caption\": \"Halloween puff pastries \\ud83e\\udd87\\ud83d\\udc7b\\nVia - anniikaly - tiktok\\n#puffpastry #halloween #halloweensnacks\",\n", + " \"code\": \"DBLEk_5AjMS\",\n", + " \"comment_count\": 3,\n", + " \"engagement_score\": 0.029510912856460098,\n", + " \"full_name\": \"Food content!! \\ud83c\\udf6a\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 4631,\n", + " \"media_id\": 3479895271763227410,\n", + " \"play_count\": 156925,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/245722266_384099476695192_438086590954522850_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=aoR18tQ_W-0Q7kNvgGTwWLI&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYCPVjzABMN-d85M5hOfq-BzeP34PWibSlOu23DlKxw6Kw&oe=671892CF&_nc_sid=026283\",\n", + " \"reshare_count\": 401,\n", + " \"thumbnail_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.29350-15/463405074_1045786373995279_4486955821649559796_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMDgweDE5MjAuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=107&_nc_ohc=P20ak3vukZ0Q7kNvgHUHAjH&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3OTg5NTI3MTc2MzIyNzQxMA%3D%3D.3-ccb7-5&oh=00_AYDeXXTzHcNeDPIDf7M1ii_i60yjH6RlQ3qMUFs4mtWWMA&oe=67189B96&_nc_sid=026283\",\n", + " \"username\": \"foodfypp\",\n", + " \"video_duration\": 16.466,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/80416440F904A93B37AC737563BFD8A7_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=100&vs=eb792a623f02d678&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC84MDQxNjQ0MEY5MDRBOTNCMzdBQzczNzU2M0JGRDhBN192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dOVDluQnZRdFBkS0JnTUNBTTl2clY5c0l1TjNicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJoLQgPnltIsDFQIoAkMzLBdAMHdLxqfvnhgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYAOURJecZM4tmDkHOgtq6CEwLzsAOJGDvEFrw04Ti_rjw&oe=6714A68A&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/35498F79D334139D18AD01F2B02E11B3_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=108&vs=be7ae81b9353dcab&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC8zNTQ5OEY3OUQzMzQxMzlEMThBRDAxRjJCMDJFMTFCM192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dOVDluQnZRdFBkS0JnTUNBTTl2clY5c0l1TjNicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJoLQgPnltIsDFQIoAkMzLBdAMHdLxqfvnhgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYBdnyo1Y2Lmnmmj16GX-0Rn-i34qpaCfBUj7SQ8bzGbsQ&oe=67149B2E&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.0025553608411661623\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"So good \\ud83e\\udd29\\ud83c\\udf4e\\nVia - createdbyjoyy - tiktok\\n#halloweensnacks\",\n", + " \"code\": \"DBCNwoxqE5L\",\n", + " \"comment_count\": 3,\n", + " \"engagement_score\": 0.00012093848262517131,\n", + " \"full_name\": \"Desserts | Sweets | Chocolate\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 3,\n", + " \"media_id\": 3477402379205758539,\n", + " \"play_count\": 24806,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/418352523_1033821481009584_7884031543928107182_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=EYgUclcPT8gQ7kNvgEMhbzi&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYCquGLNcxMtobLfLczoFCay0s2--oE-LH-ilQ33ulDJhg&oe=6718A543&_nc_sid=026283\",\n", + " \"reshare_count\": 0,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/462813285_1722800521592193_3583864867473666807_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi43MjB4MTI4MC5zZHIuZjI5MzUwLmRlZmF1bHRfY292ZXJfZnJhbWUifQ&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=109&_nc_ohc=9pPUt_0rTuMQ7kNvgGmhLdl&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3NzQwMjM3OTIwNTc1ODUzOQ%3D%3D.3-ccb7-5&oh=00_AYAVfCFvbmd9XF2ikzw2qt_FW17LfgGaFRx-IirM0QmsNw&oe=67189F69&_nc_sid=026283\",\n", + " \"username\": \"desszrt\",\n", + " \"video_duration\": 19.107,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/C144FA542E316075D79332A0CAEC6EA0_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=103&vs=f159d47bbe47308a&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9DMTQ0RkE1NDJFMzE2MDc1RDc5MzMyQTBDQUVDNkVBMF92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dDWnprUnQza2RZMVBYY0hBTnIxT19PRFo2OTBicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJriyiMjGtZMJFQIoAkMzLBdAMwhysCDEnBgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYD9AGsIPaRveupY3s0r04yeUG3HTsiqRkVxtmP6I2qHFQ&oe=6714B0AF&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/B149B598B7F741D74110B061FBBEFC94_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=109&vs=9b434d8a18f5799e&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9CMTQ5QjU5OEI3Rjc0MUQ3NDExMEIwNjFGQkJFRkM5NF92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dDWnprUnQza2RZMVBYY0hBTnIxT19PRFo2OTBicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJriyiMjGtZMJFQIoAkMzLBdAMwhysCDEnBgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYC858x2EJBmNB6q5qdZxulmcG7Ob9I5b8A8J0XYZD8EVg&oe=67148929&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.0\n", + " },\n", + " {\n", + " \"audio_type\": \"licensed_music\",\n", + " \"caption\": \"2 Weeks until Halloween \\ud83c\\udf83 \\u2022\\n\\u2022\\n\\u2022\\n#restock #halloween #snacks #candy #chocolate #snackbox #spookyseason #spooky #spookytreats #halloweenvibes #asmr #asmrsounds #satisfying #satisfyingvideos\",\n", + " \"code\": \"DBO3lSCxAQ0\",\n", + " \"comment_count\": 19,\n", + " \"engagement_score\": 0.055994998851777195,\n", + " \"full_name\": \"caitlin iola\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 4389,\n", + " \"media_id\": 3480964016556475444,\n", + " \"play_count\": 78382,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/441572903_7765971776818598_7289119026846193925_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=LltZ-u0LqE4Q7kNvgHI0PnI&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYA24ekKusEn5WDEbY1rencTbt4cAuD6cD6O2HGFcJY5_Q&oe=6718A4EE&_nc_sid=026283\",\n", + " \"reshare_count\": 129,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/463727939_397047160127318_5626998250273592330_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4yMjY4eDQwMzIuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=102&_nc_ohc=6gQpAQh3yaEQ7kNvgF6SOYi&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ4MDk2NDAxNjU1NjQ3NTQ0NA%3D%3D.3-ccb7-5&oh=00_AYCWzR74sw5KKOHivc5jphVu3nW5pqqSsRD0DHsr5gBC3A&oe=67189715&_nc_sid=026283\",\n", + " \"username\": \"caitliniola\",\n", + " \"video_duration\": 30.633,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/C9413C292E964C1C95EBFED103493D87_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=110&vs=db6c491e9e802216&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9DOTQxM0MyOTJFOTY0QzFDOTVFQkZFRDEwMzQ5M0Q4N192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dOOVVwUnNoYzBubDlmSUJBSm1yMHJ2cloyMHlicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJrTBlrzrmLMEFQIoAkMzLBdAPqIMSbpeNRgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYBzxQVeRyUi-GPWZ2RAXy5517OQpztgKSTqkPBDUViapQ&oe=6714A150&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/B14611D62FA8C47E51F859EE3F48AEA2_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=104&vs=63622fb19064929d&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9CMTQ2MTFENjJGQThDNDdFNTFGODU5RUUzRjQ4QUVBMl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dOOVVwUnNoYzBubDlmSUJBSm1yMHJ2cloyMHlicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJrTBlrzrmLMEFQIoAkMzLBdAPqIMSbpeNRgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYBkpM8w-Q9ACN7donhWtfyJytx6fQcl4AAa7zpWazxKmQ&oe=671499EE&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.0016457860223010384\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"Is it too early for Halloween content?\\ud83e\\udd2d\\ud83c\\udf83 I love making spooky/cute foods, it\\u2019s so much fun\\ud83d\\udc7b which one of these is your favorite?\\u2063\\n\\u2063\\nPaired my snack with a Beetlejuice themed @fanta_ch \\ud83c\\udf79have you seen the movie Beetlejuice Beetlejuice yet?\\ud83e\\udd70 #ad #BeetlejuiceXFanta #FantaSwitzerland #FANTAsticHalloween \\u2063#Beetlejuice#Beetlejuice\\n\\u2063\\n#breakfastideas #breakfast #veganbreakfast #veganbreakfastideas #halloween #halloweenfood #spooky #spookyseason #spookyfood #toasttuesday #toastideas #vegantoast #veganmealideas #cutefood #halloweensnacks #swissvegan #veganschweiz\",\n", + " \"code\": \"DA3uj_OMNbj\",\n", + " \"comment_count\": 264,\n", + " \"engagement_score\": 0.0464307329578981,\n", + " \"full_name\": \"Ilona Niina\\ud83c\\udde8\\ud83c\\udded\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 73494,\n", + " \"media_id\": 3474450420744640227,\n", + " \"play_count\": 1582874,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/296153555_383989050485949_2562226616155276624_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=l4nYd6Oun1kQ7kNvgEQjaJa&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYDKcutrp06Np2tV_ARnO1XcBR7jst_1Mhxrs0snYNTMeA&oe=6718A571&_nc_sid=026283\",\n", + " \"reshare_count\": 9334,\n", + " \"thumbnail_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.29350-15/462367954_1185642485856603_322400536077445424_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4yMTk4eDM5MDguc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=vXory_jVkUgQ7kNvgH2q8e0&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3NDQ1MDQyMDc0NDY0MDIyNw%3D%3D.3-ccb7-5&oh=00_AYDKKcl20lUo1k4ZJAwExDew2CHxXLH7Vrw-LTsbD2apeA&oe=6718958E&_nc_sid=026283\",\n", + " \"username\": \"veganiina\",\n", + " \"video_duration\": 22.833,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/9C47B08B28403455CBA855F6B64299A7_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=108&vs=bd0dea3de804bcad&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC85QzQ3QjA4QjI4NDAzNDU1Q0JBODU1RjZCNjQyOTlBN192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dIMmhrUnNreVREdzFOZ0tBT3ZBbjc2NEtiVXdicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJobSlsGyhNoBFQIoAkMzLBdANtU_fO2RaBgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYAxSA6aSHVjoxoIX7YwfLFh7p-kFWFQ3JqB1aQiN8ErBA&oe=6714A6DA&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/E34285DFEE5E9C902932E2D141D2A8B2_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=103&vs=bb90a9f8f3463f40&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9FMzQyODVERkVFNUU5QzkwMjkzMkUyRDE0MUQyQThCMl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dIMmhrUnNreVREdzFOZ0tBT3ZBbjc2NEtiVXdicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJobSlsGyhNoBFQIoAkMzLBdANtU_fO2RaBgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYA45AzmMsqYpax6rg512cax7bC6AhVOPEtc9q4eTLAr7g&oe=67148867&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.005896868607355986\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"So good \\ud83e\\udd29\\ud83c\\udf4e\\nVia - createdbyjoyy - tiktok\\n#halloweensnacks\",\n", + " \"code\": \"DA7-1zbxRhm\",\n", + " \"comment_count\": 62,\n", + " \"engagement_score\": 0.06375733232509502,\n", + " \"full_name\": \"Food content!! \\ud83c\\udf6a\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 98986,\n", + " \"media_id\": 3475647913689159782,\n", + " \"play_count\": 1552543,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/245722266_384099476695192_438086590954522850_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=aoR18tQ_W-0Q7kNvgGTwWLI&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYCPVjzABMN-d85M5hOfq-BzeP34PWibSlOu23DlKxw6Kw&oe=671892CF&_nc_sid=026283\",\n", + " \"reshare_count\": 17748,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/462654564_1305485117470557_158623042827495594_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMDgweDE5MjAuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=109&_nc_ohc=MCHbc3QDJiQQ7kNvgEKMlxm&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3NTY0NzkxMzY4OTE1OTc4Mg%3D%3D.3-ccb7-5&oh=00_AYC2yXdmcA3vgUbaZU-OEXzLZBum3xA0dPPBF8cNrBzB_g&oe=67187EF4&_nc_sid=026283\",\n", + " \"username\": \"foodfypp\",\n", + " \"video_duration\": 19.033,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/B648675D818AE81F90B895530C73C69F_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=101&vs=3108fa86e3539c8&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9CNjQ4Njc1RDgxOEFFODFGOTBCODk1NTMwQzczQzY5Rl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dJX1JrUnZnMFJPTWlqTURBRGlOU2VqVk1vdFVicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJraC9O_Tv6kEFQIoAkMzLBdAMwhysCDEnBgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYBhCuA-voOU_CId-gew5X79YjfFkzJyCObFvx76HUPftg&oe=67148AB1&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/0C409527184AB00A527C168A61EF91AD_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=104&vs=5a9aa0f9301b252f&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC8wQzQwOTUyNzE4NEFCMDBBNTI3QzE2OEE2MUVGOTFBRF92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dJX1JrUnZnMFJPTWlqTURBRGlOU2VqVk1vdFVicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJraC9O_Tv6kEFQIoAkMzLBdAMwhysCDEnBgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYCLIu6kP7QEfbekJ33rOGcQxrAbL-tARX7WHOTF19jfLg&oe=6714873A&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.011431567434847216\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"The only spider-related thing I can support are these spooky movie night snackies \\ud83e\\udde1\\ud83c\\udf83\\n\\n\\ud83c\\udfa5\\ud83d\\udd77\\ufe0f via @sophie.diyguru \\n\\n#halloweensnacks #spookysnacks #movienighttreat #movienightsnacks #festivefood\",\n", + " \"code\": \"DA82FR9pSTZ\",\n", + " \"comment_count\": 1,\n", + " \"engagement_score\": 0.05603136931173253,\n", + " \"full_name\": \"Girls' Life Magazine\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 543,\n", + " \"media_id\": 3475890869820007641,\n", + " \"play_count\": 9691,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/12552346_1509861505986363_1590274444_a.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=103&_nc_ohc=no_ETbGn-rYQ7kNvgFQpuYv&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYDhpgW6JyEcPR19fttBYMj_x6cVjFW6C8f11Eu5WbLFVA&oe=6718A81E&_nc_sid=026283\",\n", + " \"reshare_count\": 107,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/462225757_2591849341012985_7254678623297557105_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi43MjB4MTI4MC5zZHIuZjI5MzUwLmRlZmF1bHRfY292ZXJfZnJhbWUifQ&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=105&_nc_ohc=Jpmw3UVAhr0Q7kNvgFh57lJ&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3NTg5MDg2OTgyMDAwNzY0MQ%3D%3D.3-ccb7-5&oh=00_AYDvqkBUxrtr3ec-1Iatm-T_8cYN-xyc_QW73nFlW8_kMQ&oe=67189684&_nc_sid=026283\",\n", + " \"username\": \"girlslifemag\",\n", + " \"video_duration\": 5.966,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/FA46B68FDF3BB7429B670451D98414BB_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=111&vs=43176259516e93ff&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9GQTQ2QjY4RkRGM0JCNzQyOUI2NzA0NTFEOTg0MTRCQl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dNTFNreHVwaXgySzEtb0hBSUMxME40aE1YdzhicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJorP1Ny9yP8FFQIoAkMzLBdAF90vGp--dxgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYAzXrImAiGXeTegnxRlM-MutrlfiJUFq4pKoKSwC969gw&oe=67148473&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/4B4167BAFBBF1777E1F6FE58989BC496_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=106&vs=a9f045674e2facbf&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC80QjQxNjdCQUZCQkYxNzc3RTFGNkZFNTg5ODlCQzQ5Nl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dNTFNreHVwaXgySzEtb0hBSUMxME40aE1YdzhicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJorP1Ny9yP8FFQIoAkMzLBdAF90vGp--dxgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYCXY0BWEOWK9bsMW4zgRkLACBbfdmp9vm1IW4WDXFsPaA&oe=6714A0F4&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.011041172221648953\n", + " },\n", + " {\n", + " \"audio_type\": \"licensed_music\",\n", + " \"caption\": \"HOW TO MAKE A SPOOKY FRUIT PLATTER\\ud83d\\udc7b\\ud83c\\udf83\\ud83d\\udd77\\ufe0f\\ud83d\\udc41\\ufe0f\\n\\nWelcome to episode 23 of @melissas_healthykitchen spooky eats series. Today it\\u2019s how to make a fun and spooky fruit platter.\\n\\nSave this one for later and follow along for more spooky eats!\\n\\n1. Apple monsters: slice your apple in half, and place face down. Carefully cut out a piece in the middle, making sure to not cut all the way through. Add nut or seed butter of choice. Add sliced strawberry for tongue, almond slivers for teeth, place melted chocolate on apple and place two candy eyes for eyes.\\n2. Cutie pumpkins: peel cuties and place a piece of celery for stem\\n3. Grape eye balls: slice piece off grape off so they can stand up. Dip other side in melted chocolate and stick candy eye on top\\n4. Kiwi Frankenstein\\u2019s: peel your kiwis, leaving some of the skin on top for hair. Add some melted chocolate and stick two candy eyes on. Draw on mouth and stitching using melted chocolate. Stick two pretzel sticks on each side\\n5. Spiders on a log: spread nut or seed butter on sliced celery. Place three chocolate chips into nut/seed butter, top facing down. Draw on 4 legs on each side with melted chocolate, and cover chocolate chip with melted chocolate\\n6. Ghost bananas: peel your banana, slice in half, using a knife slice a triangle form, and remove part of banana, stick in chocolate chips for eyes + mouth\\n7. Spider blackberries: use plastic spiders and stick blackberries on top\\n8. Raspberry eyes: stick candy eyes into raspberries\\n.\\n.\\n#spookyeats #spookyfood #spookysnacks #fruitplatter #halloweensnacks #halloweenpartyfood #holidayfood #festivefood #halloweensnackboard #snackboard #spookybreakfast\",\n", + " \"code\": \"DAQulivPznq\",\n", + " \"comment_count\": 162,\n", + " \"engagement_score\": 0.027146213422569777,\n", + " \"full_name\": \"Melissa\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 19169,\n", + " \"media_id\": 3463473003507956202,\n", + " \"play_count\": 706139,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/348746985_2202240363301381_7570756924634375628_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=m2B4YBixHFIQ7kNvgHbW28c&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYDzdnMjqzyjY79YD-tAJESsvwGjSyW62gVAexI8TQvzhg&oe=67188874&_nc_sid=026283\",\n", + " \"reshare_count\": 17316,\n", + " \"thumbnail_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.29350-15/461138611_1060291539066877_8277855910183966327_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMTgweDIwOTQuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=101&_nc_ohc=v8oAhqlvV70Q7kNvgFDReap&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ2MzQ3MzAwMzUwNzk1NjIwMg%3D%3D.3-ccb7-5&oh=00_AYDi8KgE0zLZCQI9bcDoAYUeUi-98l3UBH5SQJ3St8Sg8w&oe=67187706&_nc_sid=026283\",\n", + " \"username\": \"melissas_healthykitchen\",\n", + " \"video_duration\": 35.966,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/EC4F70F8579494335954562F04E4C5AC_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=110&vs=e0858bc7238800d4&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9FQzRGNzBGODU3OTQ5NDMzNTk1NDU2MkYwNEU0QzVBQ192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dLakhlQnNvWFRNM0tEY05BRnJnejNRYXhOb1FicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJoaZ7cmTtOEGFQIoAkMzLBdAQful41P3zxgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYAoXBaItSZ89X9RlgQTa68De6qx8k7QpIvfNR2TFLa7-g&oe=67149D64&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/E6426E5A3DEFE60C0735F32BB75516AF_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=103&vs=9a6f7948ff2e4d5e&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9FNjQyNkU1QTNERUZFNjBDMDczNUYzMkJCNzU1MTZBRl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dLakhlQnNvWFRNM0tEY05BRnJnejNRYXhOb1FicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJoaZ7cmTtOEGFQIoAkMzLBdAQful41P3zxgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYADiwcIvdLss3T-Ntt1tT9GAsZLzSmBPEOXcS9mQEZwrw&oe=6714B2DF&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.02452208417889396\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"Snack ideas for a halloween party! \\ud83d\\udc7b\\ud83c\\udf6b these were all super easy to put together and perfect for a spooky little halloween party! \\n\\nYou\\u2019ll need-\\n\\nChocolate bats\\n-reeces peanut butter cups\\n-Oreos, halved\\n-sprinkle eyes\\n\\nSpiderweb cupcakes\\n-frosted cupcakes\\n-marshmallows, melted\\n\\nSpider donuts\\n-cinnamon sugar donuts \\n-Oreos\\n-black icing\\n-eye sprinkles\\n\\nHalloween cake\\n-chocolate cake from the bakery\\n-spooky cake topper- @kikipartystudio \\n\\n#halloweensnacks #snackideas #halloween #charcuterieboard #snacks #spooky #autumnmood #autumnvibes\",\n", + " \"code\": \"DA3d1QlRMwe\",\n", + " \"comment_count\": 47,\n", + " \"engagement_score\": 0.05757229777578657,\n", + " \"full_name\": \"Sheri Wilson\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 19918,\n", + " \"media_id\": 3474376840825850910,\n", + " \"play_count\": 345965,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/436300744_1430393310927990_263548536155078054_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=XWU9czdH9U8Q7kNvgHAi-bA&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYD1YkWa3YUEN3zVG885xj5pcPaUTnU3-W_S8NHSCMQgug&oe=67188FBF&_nc_sid=026283\",\n", + " \"reshare_count\": 3597,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/462521721_877462167858953_5811543829083089756_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi43MjB4MTI4MC5zZHIuZjI5MzUwLmRlZmF1bHRfY292ZXJfZnJhbWUifQ&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=105&_nc_ohc=ZrX2hLkInxkQ7kNvgE4wpsS&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ3NDM3Njg0MDgyNTg1MDkxMA%3D%3D.3-ccb7-5&oh=00_AYDoy9n7-RcU_ZAD1NkxU4zhKy8QW87IGkuyRosShEU8hw&oe=67188E5E&_nc_sid=026283\",\n", + " \"username\": \"sheri_wilson_\",\n", + " \"video_duration\": 20.033,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/FF43BFE58812F1438D5B495A6BF0B595_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=109&vs=f0032fbc0c18f8e8&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC9GRjQzQkZFNTg4MTJGMTQzOEQ1QjQ5NUE2QkYwQjU5NV92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dJZGhqUnNBbWNLT1FDY0RBTDR1MnE2Um5HUkFicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJsTGt6ms1LEEFQIoAkMzLBdANAhysCDEnBgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYBh2V33gppcMPHOUgZvVxkhr5KePblIMry9bOYewc3MxQ&oe=6714AF99&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/3B4E73E2289A656A51893E99BF5F0AB7_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=109&vs=f1620a73293a77a9&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC8zQjRFNzNFMjI4OUE2NTZBNTE4OTNFOTlCRjVGMEFCN192aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dJZGhqUnNBbWNLT1FDY0RBTDR1MnE2Um5HUkFicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJsTGt6ms1LEEFQIoAkMzLBdANAhysCDEnBgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYDhbMa7lJKKVXJYwroTqtOVAwaOzwh3fYaav9MrmHcLag&oe=67148C6D&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.010397005477432688\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"Spooky snack @pillsbury #snack #toddlersnacks #bakewithme #halloweensnacks #toddlermom #halloweenfood\",\n", + " \"code\": \"DBPZ9joyPrc\",\n", + " \"comment_count\": 50,\n", + " \"engagement_score\": 0.030355659215807965,\n", + " \"full_name\": \"The Polish Mom\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 6065,\n", + " \"media_id\": 3481115218296765148,\n", + " \"play_count\": 199798,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/454383850_1666055227513184_8833897863399704664_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=pnOEhsBK_vAQ7kNvgE1j-Yf&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYDWirWRaRLiHP1UZkG3ofetExeBSxR5-UCFzSFPWJcShQ&oe=6718951D&_nc_sid=026283\",\n", + " \"reshare_count\": 196,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/463719136_1079816350441113_7344284928674192530_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi43MjB4MTI4MC5zZHIuZjI5MzUwLmRlZmF1bHRfY292ZXJfZnJhbWUifQ&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=109&_nc_ohc=-xtYvnetRqIQ7kNvgGcQRTM&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ4MTExNTIxODI5Njc2NTE0OA%3D%3D.3-ccb7-5&oh=00_AYAv1hbnWy9TfE6GreKCydyBAAnAzWb0MdjgNz_mvwDQAw&oe=6718A332&_nc_sid=026283\",\n", + " \"username\": \"thepolishmom_\",\n", + " \"video_duration\": 61.966,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/5045AD391596F89670FCF00BCFDF9AB0_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=103&vs=91c66cf6f6feb2dc&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC81MDQ1QUQzOTE1OTZGODk2NzBGQ0YwMEJDRkRGOUFCMF92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dCcEZueHYzME9VdnhJNERBTlFCOTRmOTJQMFJicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJuTx2_LCzcYMFQIoAkMzLBdATvul41P3zxgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYBCr6S3xxGSqUfJWZFGTVvg9QRozskBQRdh-uWznNBrsg&oe=6714A647&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/4B458A5EB82A688B5A2849D15B356791_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=104&vs=58c939c16cd2f3d8&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC80QjQ1OEE1RUI4MkE2ODhCNUEyODQ5RDE1QjM1Njc5MV92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dCcEZueHYzME9VdnhJNERBTlFCOTRmOTJQMFJicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJuTx2_LCzcYMFQIoAkMzLBdATvul41P3zxgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYCwrxgkGSztddeGNaHTlb27MCj-VWDdcfCuvxxrLKGABQ&oe=6714A1EA&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.000980990800708716\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"#SponsoredByMarsWrigley \\ud83c\\udf83 Monster Mash Snack Cups \\ud83c\\udf83\\u2063\\u2063\\n\\u2063\\u2063\\nThese Halloween M&M\\u2019S\\u00ae are the perfect topping to include for your spooky Halloween movie nights!! With flavors like Pumpkin Pie, Peanut Butter Ghoul\\u2019s Blend, Peanuts Ghoul\\u2019s Blend, and Milk Chocolate Ghoul\\u2019s Blend, you will have a delicious popcorn board with toppings to share for your Halloween movie night! These are perfect for getting you into the Halloween spirit! @mmschocolate \\n.\\u2063\\u2063\\n.\\u2063\\u2063\\n.\\u2063\\u2063\\n.\\u2063\\u2063\\n#ad #halloween #halloweendiy #spookyseason #spookyszn #halloweeneveryday #halloweeniscoming #halloweenishere #halloweensnacks\",\n", + " \"code\": \"C_dQ_70go6H\",\n", + " \"comment_count\": 278,\n", + " \"engagement_score\": 0.034613103498648747,\n", + " \"full_name\": \"Kelsi Savage\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 59236,\n", + " \"media_id\": 3448987651938619015,\n", + " \"play_count\": 1711375,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/347210180_9244767625596127_5717497940764698490_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=AF5Fhs_fYMQQ7kNvgE2K4zl&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYDb4R5XBH3lrSOAz0e2RfZeGjxJctbGaZofKe7o5kVi0A&oe=67188FDD&_nc_sid=026283\",\n", + " \"reshare_count\": 27095,\n", + " \"thumbnail_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.29350-15/458185503_1681277036007141_4531932984155473571_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMjk2eDIzMDQuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=109&_nc_ohc=7Y-3EWp4GysQ7kNvgEBSoH8&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ0ODk4NzY1MTkzODYxOTAxNQ%3D%3D.3-ccb7-5&oh=00_AYBoRoajoNnpyzBsxh_GKEE2qSITKGNISVak3ZPWz-ljdw&oe=67189B46&_nc_sid=026283\",\n", + " \"username\": \"kelsimsavage\",\n", + " \"video_duration\": 32.333,\n", + " \"video_url_360\": \"No URL Available\",\n", + " \"video_url_720\": \"No URL Available\",\n", + " \"virality_score\": 0.015832298590314806\n", + " },\n", + " {\n", + " \"audio_type\": \"original_sounds\",\n", + " \"caption\": \"Back with Episode 2 of Halloween with Pretzels! \\ud83c\\udf83\\ud83d\\udc7b\\ud83e\\udd68\\nHere I\\u2019m sharing 5 more fun and spooky pretzel ideas that are super easy and quick to make! Perfect for your Halloween celebrations or a fun project with the kids.\\n\\nDon\\u2019t forget to save this post so you can revisit it later and tag a friend who you want to make these with!\\u2764\\ufe0f\\n\\nComment \\u201cSPOOKY\\u201d to get the full recipe sent to you, and be sure to check out my last reel for 7 more creative pretzel ideas for Halloween! \\ud83e\\uddd9\\u200d\\u2640\\ufe0f\\ud83d\\udd78\\ufe0f \\n\\n\\u2013 Ingredients \\u2013\\n* Pretzels\\n* Pretzel sticks\\n* White chocolate\\n* Dark chocolate\\n* Candy eyes\\n* Red candy melts/ red food coloring\\n* green candy melts/ Green food coloring\\n* Orange/Yellow & Brown skittles\\n* Halloween sprinkles\\n* Almonds\\n\\n#halloween #halloweensnacks #snacks #pretzels \\n\\nWhich is your favorite?\\ud83e\\udd68\",\n", + " \"code\": \"DBMO9fcvrKa\",\n", + " \"comment_count\": 224,\n", + " \"engagement_score\": 0.09330406147091108,\n", + " \"full_name\": \"MunchyMoodz | Easy, Healthy & Delicious Recipes\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 340,\n", + " \"media_id\": 3480222410358043290,\n", + " \"play_count\": 3644,\n", + " \"profile_pic_url\": \"https://scontent-lax3-1.cdninstagram.com/v/t51.2885-19/403894629_687200496844322_9030549871509720564_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_cat=102&_nc_ohc=DaxFHV4zNY4Q7kNvgGqecIN&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYCDrMF-QkeZXAO6elipoGQP_tbGbv14f_FllisYlxQZ1Q&oe=671885FF&_nc_sid=026283\",\n", + " \"reshare_count\": 51,\n", + " \"thumbnail_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.29350-15/463450557_833717278650762_7352201446183296663_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMDgweDE5MjAuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=106&_nc_ohc=MDrZFS-fGhQQ7kNvgGYlOXF&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ4MDIyMjQxMDM1ODA0MzI5MA%3D%3D.3-ccb7-5&oh=00_AYAjoePISSHrzPdTH4J2bTE60PdlsMo5O13NBKyGnmyHxw&oe=67189D23&_nc_sid=026283\",\n", + " \"username\": \"munchymoodz\",\n", + " \"video_duration\": 6.166,\n", + " \"video_url_360\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/984B5E60863650B5F71B4FC67014D68A_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuMzYwLmRhc2hfYmFzZWxpbmVfM192MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=109&vs=7116aba912ff7526&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC85ODRCNUU2MDg2MzY1MEI1RjcxQjRGQzY3MDE0RDY4QV92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dPTDduaHRtUHFpZWE1RUVBUHRPMXBhSzhzMDZicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJuzPqpOek5cDFQIoAkMzLBdAGKn752yLRBgSZGFzaF9iYXNlbGluZV8zX3YxEQB1_gcA&ccb=9-4&oh=00_AYBt8TfP2u4EW-WMvKsi--5swCf_dlYaDKGyBD-fhv4ZRA&oe=67149EAD&_nc_sid=1d576d\",\n", + " \"video_url_720\": \"https://scontent-lax3-2.cdninstagram.com/o1/v/t16/f1/m86/384B85629304FF479DE4D8619421DCB6_video_dashinit.mp4?efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSJ9&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=101&vs=484b1136985e5111&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC8zODRCODU2MjkzMDRGRjQ3OURFNEQ4NjE5NDIxRENCNl92aWRlb19kYXNoaW5pdC5tcDQVAALIAQAVAhg6cGFzc3Rocm91Z2hfZXZlcnN0b3JlL0dPTDduaHRtUHFpZWE1RUVBUHRPMXBhSzhzMDZicV9FQUFBRhUCAsgBACgAGAAbAogHdXNlX29pbAExEnByb2dyZXNzaXZlX3JlY2lwZQExFQAAJuzPqpOek5cDFQIoAkMzLBdAGKn752yLRBgSZGFzaF9iYXNlbGluZV8xX3YxEQB1_gcA&ccb=9-4&oh=00_AYBi5wDuShfXCnaCZKqEOC27RgqUaFMkEG7pDSbXLPb3IA&oe=67148CED&_nc_sid=1d576d\",\n", + " \"virality_score\": 0.013995609220636664\n", + " },\n", + " {\n", + " \"audio_type\": \"licensed_music\",\n", + " \"caption\": \"FRANKENSTEIN GUACAMOLE HEAD! \\ud83d\\udda4\\ud83d\\udc9a this was so easy and so fun to serve at your Halloween party! #halloweensnacks\",\n", + " \"code\": \"C_gGBKHPq2B\",\n", + " \"comment_count\": 63,\n", + " \"engagement_score\": 0.023059542622800945,\n", + " \"full_name\": \"Ryan Armendariz\",\n", + " \"has_audio\": true,\n", + " \"like_count\": 8828,\n", + " \"media_id\": 3449783782423309697,\n", + " \"play_count\": 382835,\n", + " \"profile_pic_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.2885-19/382715197_870519510669394_5841983755351127471_n.jpg?stp=dst-jpg_e0_s150x150&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_ohc=_3g7HuaIgJEQ7kNvgG_bUZC&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&oh=00_AYBfzEIGplT6d9RUMv3R1Szm556mkm8u7ttG15WIDGk7wA&oe=67189100&_nc_sid=026283\",\n", + " \"reshare_count\": 11492,\n", + " \"thumbnail_url\": \"https://scontent-lax3-2.cdninstagram.com/v/t51.29350-15/458372323_1776513799844520_8149213031542621134_n.jpg?stp=dst-jpg_e15_p360x360&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMjg0eDIyODIuc2RyLmYyOTM1MC5kZWZhdWx0X2NvdmVyX2ZyYW1lIn0&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=100&_nc_ohc=1ZtXBV0ri5UQ7kNvgFTjbwm&_nc_gid=93db8eed31794f62919736b7364f7537&edm=AL2I2h8BAAAA&ccb=7-5&ig_cache_key=MzQ0OTc4Mzc4MjQyMzMwOTY5Nw%3D%3D.3-ccb7-5&oh=00_AYA2n44KBKr2PZcIlK1bs1mxadG9oR7TjZg_YkBVMdIy2g&oe=67187623&_nc_sid=026283\",\n", + " \"username\": \"itseverydayryan\",\n", + " \"video_duration\": 15.92,\n", + " \"video_url_360\": \"No URL Available\",\n", + " \"video_url_720\": \"No URL Available\",\n", + " \"virality_score\": 0.030018154035028145\n", + " }\n", + " ]\n", + "}\n" + ] + } + ], + "source": [ + "import json\n", + "response = client.executions.transitions.list(execution_id=execution.id).items[0].output\n", + "print(json.dumps(response, indent=4))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "julep", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/04-hook-generator-trending-reels.py b/cookbooks/04-hook-generator-trending-reels.py new file mode 100644 index 000000000..84c5d9688 --- /dev/null +++ b/cookbooks/04-hook-generator-trending-reels.py @@ -0,0 +1,376 @@ + + +# %% +# Global UUID is generated for agent and task +import time, yaml +from dotenv import load_dotenv +import os +import uuid +load_dotenv(override=True) + +AGENT_UUID = uuid.uuid4() +TASK_UUID = uuid.uuid4() +RAPID_API_KEY = os.getenv('RAPID_API_KEY') +RAPID_API_HOST = os.getenv('RAPID_API_HOST') +JULEP_API_KEY = os.getenv('JULEP_API_KEY') or os.getenv('JULEP_API_KEY_LOCAL') + +print(f'AGENT_UUID: {AGENT_UUID}') +print(f'TASK_UUID: {TASK_UUID}') +print(f'JULEP_API_KEY: {JULEP_API_KEY}') +print(f'RAPID_API_KEY: {RAPID_API_KEY}') +print(f'RAPID_API_HOST: {RAPID_API_HOST}') + + +# ### Creating Julep Client with the API Key + +from julep import Client +# # Create a client +client = Client(api_key=JULEP_API_KEY,environment="dev") + +# Creating an agent for handling persistent sessions +agent = client.agents.create_or_update( + agent_id=AGENT_UUID, + name="Session Manager", + about="An AI agent specialized in managing persistent sessions and context.", + model="gpt-4o", +) + +# ### Creating a Document for Hooks +hooks_data = [ + {"categories": "Price-Focused", + "content": [ + "Save both time and money with [product].", + "Save both time and money on [task].", + "Is [product] worth the price? Let’s find out!", + "Why is it challenging and costly to find a good [product category]?", + "Searching for an affordable [product type]? Check this out!", + "How to locate an affordable [service].", + "I can’t believe this costs only [price].", + "Don't waste your money on [X]; instead buy [X]." + ]}, + {"categories": "Informative", + "content": [ + "What’s inside [product]?", + "Common questions I get about [product].", + "Now you can get X delivered right to your door.", + "Thinking of trying [product type]?", + "Allow me to introduce you to [product].", + "[category] Tip #X.", + "Here’s how to achieve [value prop].", + "I know this sounds unbelievable, but…", + "How to get X done in just 10 minutes.", + "How to find time for X.", + "Here’s my biggest life hack for X.", + "Add more [value prop] to your day." + ]}, + {"categories": "Versus the Alternative (or Competition)", + "content": ["Before you try [product type], watch this first.", + "Hate [the worse alternative]? Give this a try!", + "Thinking about [worse alternative]?", + "Instead of [worse alternative], try this.", + "Still using [the worse alternative to the product]? Watch this.", + "Something that’s always annoyed me about X.", + "Don’t buy X that doesn’t work. Try this instead.", + "I tested every [product category] so you don’t have to: here’s what I found.", + "Stop doing [worse alternative]. Try [product] instead.", + "Dealing with [negative experience]? I used [product] to help.", + "Why millennials are switching to [product].", + "How to do X without [worse alternative].", + "I only get my [product category] from [brand name].", + "I no longer buy my [product category] from [worse alternative].", + "[Worse alternative] can be difficult to deal with.", + "Your new X alternative.", + "[value prop] without the [negative side effect].", + "I kept experiencing [pain point], so I tried this instead!", + "If you have [pain point] — you need to see this!", + "I wanted to stop doing X, so I tried this instead.", + "Your X isn't [adverb]; you just need X.", + "My secret to [popular trend] revealed!" + ]}, + {"categories": "User Experience", + "content": [ + "Guys, it’s here…", + "What I ordered vs. what I received.", + "[Product] unboxing.", + "Let’s create X with [product].", + "POV: You tried [product].", + "A day in the life of X.", + "Get ready with me to do [task].", + "“Put a finger down” [product category] edition.", + "Trying home remedies for X.", + "[Product category] ASMR." + ]}, + {"categories": "Responding to Hype", + "content": [ + "TikTok made me try [product].", + "Things TikTok made me try #13.", + "This [product type] is going viral on [social media platform].", + "I tested the viral [product type] to see if it lives up to the hype.", + "This [product type] has over 5,000 reviews… let’s see if it’s worth it.", + "[Publication] can’t stop raving about us.", + "It’s so good it sold out in a week." + ]}, + {"categories": "It’s Easier", + "content": [ + "Are you [accomplishing the goal optimally]?", + "Life Hack: Try [product] for [pain point].", + "My go-to [product] for [pain point].", + "How to easily [task].", + "[Task] has never been easier than with [product].", + "My favorite [product] to make [hard task] simpler.", + "Here’s my top product for [task].", + "Struggling to do [task]?", + "I’ve been struggling with [task], but [product] has really helped.", + "Easiest way to do [task]?", + "Make your week easier.", + "Why adults avoid [task]… [product] makes it easy.", + "[Product] made [task] so much easier! You’ve got to try it.", + "When I use [product], it’s one less thing I have to worry about.", + "How to do [X] in half the time.", + "This trick/hack/method will save you hours...", + "Easy hack to [X]...", + "Simple [X] that will make you [X]." + ]}, + {"categories": "Lists", + "content": [ + "5 Ways [product] Helps with [pain point].", + "3 reasons to buy [product].", + "3 reasons to try [service].", + "Get [value prop] in 3 steps.", + "Here are 3 ways [worse alternative] affects your life.", + "5 things you didn’t know about [topic].", + "The ultimate [X] checklist to [action].", + "Reasons why [X].", + "Here are the 3 best ways to [X].", + "Here are [X] mistakes you might be making...", + "If you want [X], do these 5 things..." + ]}, + {"categories": "The Best", + "content": [ + "The internet’s #1 [product type].", + "The best way to [accomplish the goal of the product].", + "What makes [the product type] the best?", + "My skin has never looked better with [product].", + "The best way to find X in 2022.", + "[Product] changed how I do [task], and I’m never going back.", + "Why is [product] so good though?", + "After hours of researching, I found the best [product type] for [task].", + "I found the best [product category] for [value prop].", + "This is going to blow your mind.", + "How I got [X] in 24 hours.", + "Must-have [products] for [X].", + "The best [target audience] know something that you don't." + ]}, + {"categories": "Other Video Hooks that Address Viewers Directly", + "content": [ + "Hey, [customer type], you’ve got to try this.", + "People looking for [product category], stop scrolling.", + "Wait, have you tried X?", + "Take control of your X with [product].", + "Imagine if X was also X.", + "Watch this if you X." + ]}, + {"categories": "Facts & Stats", + "content": [ + "PSA: [statement about product category].", + "Did you know? [fact about product category].", + "I just found out [fact about product category].", + "Are you one of [fact about product category] people who do X?", + "New customers get [discount].", + "Take [discount] off when you try [product].", + "I didn’t know X could be related to X.", + "Why is it important to [do product-related task]?", + "99\% of your [target audience] don't. To be the 1% you need to [X].", + "This [product] is the secret to [X]." + ]}, + {"categories": "Curiosity & Engagement", + "content": [ + "Is there anything worse than [X]?", + "I will never [adjective] from learning this.", + "X people start scrolling. I have the perfect [X] for you.", + "Here's a challenge for you...", + "There's nothing more painful than [X].", + "What would you do if...", + "Watch till the end…" + ]}, + {"categories": "Negative Hooks", + "content": [ + "Why you're failing at [activity].", + "The worst mistake you can make in [subject].", + "Avoid these common pitfalls in [topic].", + "The dark side of [popular trend].", + "Don't be fooled by [misconception].", + "Why [common practice] is ruining your [outcome].", + "What no one tells you about [issue].", + "The hidden dangers of [activity].", + "Stop doing this if you want to succeed in [field].", + "The ugly truth about [common belief].", + "Why [habit] is wasting your time.", + "Exposing the myths about [topic].", + "The biggest regret you'll have in [situation].", + "How [action] is destroying your [goal].", + "The shocking reality of [popular topic].", + "Why [trend] is a bad idea.", + "The real reason you're not seeing results in [field].", + "The downside of [seemingly positive aspect].", + "What you should never do in [activity].", + "Why [common advice] is actually harmful." + ]} +] + +hooks_doc_content = [] +for category in hooks_data: + hooks_doc_content.extend(category['content']) + +doc = client.agents.docs.create( + agent_id=AGENT_UUID, title="hooks_doc", content=hooks_doc_content) +print(doc.id) + +# ####Listing the Documents +for doc in client.agents.docs.list(agent_id=AGENT_UUID): + print(doc.content) + +# Defining a Task +import yaml +task_def = yaml.safe_load(f""" +name: Trending Reels Hook Generator + +tools: +- name: api_tool_call + type: api_call + api_call: + method: GET + url: "https://instagram-scraper-api3.p.rapidapi.com/reels_by_keyword" + headers: + x-rapidapi-key: "{RAPID_API_KEY}" + x-rapidapi-host: "{RAPID_API_HOST}" + follow_redirects: true + +- name: get_hooks_doc + type: system + system: + resource: agent + subresource: doc + operation: list + +main: + +- tool: api_tool_call + arguments: + params: + query: "inputs[0].topic" + +- evaluate: + summary: "list({{ + 'caption': ((clip.get('media') or {{}}).get('caption') or {{}}).get('text') or 'No Caption Available', + 'code': (clip.get('media') or {{}}).get('code') or 'No Code Available', + 'media_id': ((clip.get('media') or {{}}).get('caption') or {{}}).get('media_id', 0), + 'video_duration': (clip.get('media') or {{}}).get('video_duration', 0), + 'thumbnail_url': (clip.get('media') or {{}}).get('image_versions2', {{}}).get('candidates', [{{}}])[0].get('url') or 'No Thumbnail URL Available', + 'video_url_360': [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 360][0] if [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 360] else 'No URL Available', + 'video_url_720': [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 720][0] if [video.get('url') for video in (clip.get('media') or {{}}).get('video_versions', [{{}}]) if video.get('width') == 720] else 'No URL Available', + 'play_count': (clip.get('media') or {{}}).get('play_count', 0), + 'like_count': (clip.get('media') or {{}}).get('like_count', 0), + 'comment_count': (clip.get('media') or {{}}).get('comment_count', 0), + 'reshare_count': (clip.get('media') or {{}}).get('reshare_count', 0), + 'has_audio': (clip.get('media') or {{}}).get('has_audio', False), + 'audio_type': (clip.get('media') or {{}}).get('clips_metadata', {{}}).get('audio_type', 'No Audio Type Available'), + 'username': (clip.get('media') or {{}}).get('user', {{}}).get('username') or 'No Username Available', + 'full_name': (clip.get('media') or {{}}).get('user', {{}}).get('full_name') or 'No Full Name Available', + 'profile_pic_url': (clip.get('media') or {{}}).get('user', {{}}).get('profile_pic_url') or 'No Profile Pic URL Available', + 'virality_score': (clip.get('media') or {{}}).get('reshare_count', 0) / ((clip.get('media') or {{}}).get('play_count', 1) if (clip.get('media') or {{}}).get('play_count', 0) > 0 else 1), + 'engagement_score': (clip.get('media') or {{}}).get('like_count', 0) / ((clip.get('media') or {{}}).get('play_count', 1) if (clip.get('media') or {{}}).get('play_count', 0) > 0 else 1) + }} for clip in _['json']['data']['reels_serp_modules'][0]['clips'])" + +- over: _['summary'] + parallelism: 4 + map: + prompt: + - role: system + content: >- + You are a skilled agent tasked with creating a single description for each trending real estate reel for the given topic: {{{{inputs[0].topic}}}}. + Use information gathered from the following data sources to gather the most relevant information: + + Search Results: {{{{_['caption']}}}} + Virality Score: {{{{_['virality_score']}}}} + Engagement Score: {{{{_['engagement_score']}}}} + + Provide a json repsonse containing the caption, virality score, enagement score and one-liner description for the reel. + unwrap: true + +- evaluate: + summary: outputs[1]['summary'] + description: list(load_json(res.replace("```json", "").replace("```", "")) for res in _) + +- tool: get_hooks_doc + arguments: + agent_id: "'{AGENT_UUID}'" + +- evaluate: + hooks_doc: _[0]['content'] + +- over: outputs[3]['description'] + parallelism: 4 + map: + prompt: + - role: system + content: >- + You are a skilled content creator tasked with generating 3 engaging video hooks for each reel having its description and caption. Use the following document containing hook templates to create effective hooks: + + {{{{_.hooks_doc}}}} + + Here are the caption and description to create hooks for: + + Caption: {{{{_['caption']}}}} + Description: {{{{_['description']}}}} + Virality Score: {{{{_['virality_score']}}}} + Engagement Score: {{{{_['engagement_score']}}}} + + Your task is to generate 3 hooks (for the reel) by adapting the most suitable templates from the document. Each hook should be no more than 1 sentence long and directly relate to its corresponding idea. + + Basically, all the ideas are taken from a search about this topic, which is {{{{inputs[0].topic}}}}. You should focus on this while writing the hooks. + + Ensure that each hook is creative, engaging, and relevant to its idea while following the structure of the chosen template. + + Provide a json repsonse containing the caption, virality score, enagement score, description and the list of 3 hooks for the reel. + unwrap: true + +- evaluate: + summary: outputs[3]['summary'] + hooks: list(load_json(res.replace("```json", "").replace("```", "")) for res in _) +""") + +# Creating/Updating a task +task = client.tasks.create_or_update( + task_id=TASK_UUID, + agent_id=AGENT_UUID, + **task_def +) + +# Creating an execution of the task +execution = client.executions.create( + task_id=task.id, + input={ + "topic": "halloween snacks" + } +) + +# Waiting for the execution to complete +import time +time.sleep(120) + +# Lists all the task steps that have been executed up to this point in time +transitions = client.executions.transitions.list(execution_id=execution.id).items + +# Transitions are retreived in reverse chronological order +for transition in reversed(transitions): + print("Transition type: ", transition.type) + print("Transition output: ", transition.output) + print("-"*50) + +import json +response = client.executions.transitions.list(execution_id=execution.id).items[0].output +print(json.dumps(response, indent=4)) + + + diff --git a/cookbooks/05-Basic_Agent_Creation_and_Interaction.py b/cookbooks/05-Basic_Agent_Creation_and_Interaction.py deleted file mode 100644 index c701471f7..000000000 --- a/cookbooks/05-Basic_Agent_Creation_and_Interaction.py +++ /dev/null @@ -1,71 +0,0 @@ -import uuid -from julep import Client - -# Global UUID is generated for agent -AGENT_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an "agent" -name = "Jarvis" -about = "A friendly and knowledgeable AI assistant." -default_settings = { - "temperature": 0.7, - "top_p": 1, - "min_p": 0.01, - "presence_penalty": 0, - "frequency_penalty": 0, - "length_penalty": 1.0, - "max_tokens": 150, -} - -# Create the agent -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name=name, - about=about, - model="gpt-4-turbo", -) - -print(f"Agent created with ID: {agent.id}") - -# Create a session for interaction -session = client.sessions.create( - agent=agent.id, - context_overflow="adaptive" -) - -print(f"Session created with ID: {session.id}") - -# Function to chat with the agent -def chat_with_agent(message): - message = { - "role": "user", - "content": message, - } - # TODO: message validation error - response = client.sessions.chat( - session_id=session.id, - messages=[message], - ) - return response.choices[0].message.content - -# Demonstrate basic interaction -print("Agent: Hello! I'm Jarvis, your AI assistant. How can I help you today?") - -while True: - user_input = input("You: ") - if user_input.lower() in ['exit', 'quit', 'bye']: - print("Agent: Goodbye! It was nice chatting with you.") - break - - response = chat_with_agent(user_input) - print(f"Agent: {response}") - -# Optional: Retrieve chat history -history = client.sessions.messages.list(session_id=session.id) -print("\nChat History:") -for message in history.items: - print(f"{message.role}: {message.content}") \ No newline at end of file diff --git a/cookbooks/05-video-processing-with-natural-language.ipynb b/cookbooks/05-video-processing-with-natural-language.ipynb new file mode 100644 index 000000000..37fa5fb5d --- /dev/null +++ b/cookbooks/05-video-processing-with-natural-language.ipynb @@ -0,0 +1,579 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task Definition: Video Processing with Natural Language\n", + "\n", + "### Overview\n", + "\n", + "This task is leverages the cloudinary `integration` tool, and combines it with a prompt step to convert a natural language instructions and apply them to a given video.\n", + "\n", + "### Task Tools:\n", + "\n", + "**Cloudinary**: An `integration` type tool that can upload and transform media files.\n", + "\n", + "### Task Input:\n", + "\n", + "**video_url**: The URL of the video to transform.\n", + "\n", + "**public_id**: The public id of the video to transform.\n", + "\n", + "**transformation_prompt**: The natural language instructions to apply to the video.\n", + "\n", + "### Task Output:\n", + "\n", + "**transformed_video_url**: The URL of the transformed video.\n", + "\n", + "### Task Flow\n", + "\n", + "1. **Input**: The user provides a URL to a video and transoformation instrucitons (in natural language) to apply to the video.\n", + "\n", + "2. **Cloudinary Tool Integration**: The `cloudinary_upload` tool is called to upload the video to cloudinary.\n", + "\n", + "3. **Prompt Step**: The prompt step is used to convert the natural language instructions into a json of transformation instructions that are compatible with cloudinary's API. In this step, `gemini-1.5-pro` is used as the model due to its ability to read video files.\n", + "\n", + "4. **Cloudinary Tool Integration**: The `cloudinary_upload` tool is called again to apply the transformation instructions to the video.\n", + "\n", + "5. **Output**: The final output is the URL of the transformed video.\n", + "\n", + "```plaintext\n", + "+----------+ +------------+ +------------+ +-----------+\n", + "| Video | | Cloudinary | | Prompt | | Processed |\n", + "| Input | --> | Upload | --> | Step | --> | Video |\n", + "| | | | | (Gemini) | | |\n", + "+----------+ +------------+ +------------+ +-----------+\n", + " | | | |\n", + " | | | |\n", + " v v v v\n", + "\"video.mp4 + Upload video Generate JSON \"video.mp4 with\n", + "NL prompt\" transformations blur & overlay\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Installing the Julep Client" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --upgrade julep --quiet" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "\n", + "# NOTE: these UUIDs are used in order not to use the `create_or_update` methods instead of\n", + "# the `create` methods for the sake of not creating new resources every time a cell is run.\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a Julep client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Create agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=\"Spiderman\",\n", + " about=\"AI that can crawl the web and extract data\",\n", + " model=\"gpt-4o\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "\n", + "cloudinary_api_key = os.getenv(\"CLOUDINARY_API_KEY\")\n", + "cloudinary_api_secret = os.getenv(\"CLOUDINARY_API_SECRET\")\n", + "cloudinary_cloud_name = os.getenv(\"CLOUDINARY_CLOUD_NAME\")\n", + "\n", + "# Define the task\n", + "task_def = yaml.safe_load(f\"\"\"\n", + "name: Video Processing With Natural Language\n", + "\n", + "input_schema:\n", + " type: object\n", + " properties:\n", + " video_url:\n", + " type: string\n", + " description: The url of the file to upload\n", + " public_id:\n", + " type: string\n", + " description: The public id of the file to upload\n", + " transformation_prompt:\n", + " type: string\n", + " description: The prompt for the transformations to apply to the file\n", + "\n", + "tools:\n", + "- name: cloudinary_upload\n", + " type: integration\n", + " integration:\n", + " provider: cloudinary\n", + " method: media_upload\n", + " setup:\n", + " cloudinary_api_key: \"{cloudinary_api_key}\"\n", + " cloudinary_api_secret: \"{cloudinary_api_secret}\"\n", + " cloudinary_cloud_name: \"{cloudinary_cloud_name}\"\n", + "\n", + "main:\n", + "- tool: cloudinary_upload\n", + " arguments:\n", + " file: '_0.video_url'\n", + " public_id: '_0.public_id'\n", + " upload_params:\n", + " resource_type: \"'video'\"\n", + "\n", + "- prompt:\n", + " - role: user\n", + " content:\n", + "\n", + " - type: text\n", + " text: |-\n", + " You are a Cloudinary expert. You are given a medial url. it might be an image or a video.\n", + " You need to come up with a json of transformations to apply to the given media.\n", + " Overall the json could have multiple transformation json objects.\n", + " Each transformation json object can have the multiple key value pairs.\n", + " Each key value pair should have the key as the transformation name like \"aspect_ratio\", \"crop\", \"width\" etc and the value as the transformation parameter value.\n", + " Given below is an example of a transformation json list. Don't provide explanations and/or comments in the json.\n", + " ```json\n", + " [\n", + " {{\n", + " \"aspect_ratio\": \"1.0\",\n", + " \"width\": 250,\n", + " }},\n", + " {{\n", + " \"fetch_format\": \"auto\"\n", + " }},\n", + " {{\n", + " \"overlay\":\n", + " {{\n", + " \"url\": \"\"\n", + " }}\n", + " }},\n", + " {{\n", + " \"flags\": \"layer_apply\"\n", + " }}\n", + " ]\n", + " ```\n", + " - type: image_url\n", + " image_url:\n", + " url: \"{{{{_.url}}}}\"\n", + "\n", + " - type: text\n", + " text: |-\n", + " Hey, check the video above, I need to apply the following transformations using cloudinary.\n", + " {{{{_0.transformation_prompt}}}}\n", + "\n", + " unwrap: true\n", + " settings:\n", + " model: gemini/gemini-1.5-pro\n", + "\n", + "# Extract the json from the model's response\n", + "- evaluate:\n", + " model_transformation: load_json(\n", + " _[_.find(\"```json\")+7:][:_[_.find(\"```json\")+7:].find(\"```\")])\n", + "\n", + "- tool: cloudinary_upload\n", + " arguments:\n", + " file: '_0.video_url'\n", + " public_id: '_0.public_id'\n", + " upload_params:\n", + " transformation: '_.model_transformation'\n", + " resource_type: \"'video'\"\n", + "\n", + "- evaluate:\n", + " transformed_video_url: '_.url'\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The reason for using the quadruple curly braces `{{{{}}}}` for the jinja template is to avoid conflicts with the curly braces when using the `f` formatted strings in python. [More information here](https://stackoverflow.com/questions/64493332/jinja-templating-in-airflow-along-with-formatted-text)\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# creating the task object\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'1021a8d6-5050-48c3-b23e-6d96578d1026'" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# creating an execution object\n", + "transformation_prompt = \"\"\"\n", + "1- I want to add an overlay an the following image to the video, and apply a layer apply flag also. Here's the image url:\n", + "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png\n", + "\n", + "2- I also want you to to blur the video, and add a fade in and fade out effect to the video with a duration of 3 seconds each.\n", + "\"\"\"\n", + "\n", + "input_video_url = \"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4\"\n", + "\n", + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"video_url\": input_video_url,\n", + " \"public_id\": \"video_test2\",\n", + " \"transformation_prompt\": transformation_prompt,\n", + " }\n", + ")\n", + "execution.id" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'base64': None, 'meta_data': {'api_key': '518455844981529', 'asset_folder': '', 'asset_id': '069d70c84e88ee9293a1d753b3ca3898', 'audio': {'bit_rate': '191999', 'channel_layout': 'stereo', 'channels': 2, 'codec': 'aac', 'frequency': 44100}, 'bit_rate': 1197518, 'bytes': 2252313, 'created_at': '2024-11-20T08:55:44Z', 'display_name': 'video_test2', 'duration': 15.046531, 'etag': '9e87559cba36e6d07f7435c2a8081b3a', 'format': 'mp4', 'frame_rate': 24.0, 'height': 720, 'is_audio': False, 'nb_frames': 361, 'original_filename': 'ForBiggerMeltdowns', 'overwritten': True, 'pages': 0, 'placeholder': False, 'playback_url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/sp_auto/v1732200108/video_test2.m3u8', 'resource_type': 'video', 'rotation': 0, 'signature': 'e65a0d39a282fc8bd4dd3288d7d35dfffdadc2a8', 'tags': [], 'type': 'upload', 'url': 'http://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200108/video_test2.mp4', 'version': 1732200108, 'version_id': 'a9fccdf7b310edb3c2ff5ffe9335f7f8', 'video': {'bit_rate': '1002377', 'codec': 'h264', 'level': 31, 'pix_format': 'yuv420p', 'profile': 'High', 'time_base': '1/48'}, 'width': 1280}, 'public_id': 'video_test2', 'url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200108/video_test2.mp4'}\n" + ] + } + ], + "source": [ + "# Get execution details\n", + "execution = client.executions.get(execution.id)\n", + "# Print the output\n", + "print(execution.output)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition type: init\n", + "Transition output: {'public_id': 'video_test2', 'transformation_prompt': \"\\n1- I want to add an overlay an the following image to the video, and apply a layer apply flag also. Here's the image url:\\nhttps://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png\\n\\n2- I also want you to to blur the video, and add a fade in and fade out effect to the video with a duration of 3 seconds each.\\n\", 'video_url': 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'base64': None, 'meta_data': {'api_key': '518455844981529', 'asset_folder': '', 'asset_id': '069d70c84e88ee9293a1d753b3ca3898', 'audio': {'bit_rate': '191999', 'channel_layout': 'stereo', 'channels': 2, 'codec': 'aac', 'frequency': 44100}, 'bit_rate': 1197518, 'bytes': 2252313, 'created_at': '2024-11-20T08:55:44Z', 'display_name': 'video_test2', 'duration': 15.046531, 'etag': '9e87559cba36e6d07f7435c2a8081b3a', 'format': 'mp4', 'frame_rate': 24.0, 'height': 720, 'is_audio': False, 'nb_frames': 361, 'original_filename': 'ForBiggerMeltdowns', 'overwritten': True, 'pages': 0, 'placeholder': False, 'playback_url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/sp_auto/v1732200198/video_test2.m3u8', 'resource_type': 'video', 'rotation': 0, 'signature': 'f46b7d3d234cd8d4c0f15423b507768df17f2ff6', 'tags': [], 'type': 'upload', 'url': 'http://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200198/video_test2.mp4', 'version': 1732200198, 'version_id': '5ed8eae9dc683377dc4713767bf729f0', 'video': {'bit_rate': '1002377', 'codec': 'h264', 'level': 31, 'pix_format': 'yuv420p', 'profile': 'High', 'time_base': '1/48'}, 'width': 1280}, 'public_id': 'video_test2', 'url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200198/video_test2.mp4'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: ```json\n", + "[\n", + " {\n", + " \"overlay\": {\n", + " \"url\": \"https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png\"\n", + " }\n", + " },\n", + " {\n", + " \"flags\": \"layer_apply\"\n", + " },\n", + " {\n", + " \"effect\": \"blur:100\"\n", + " },\n", + " {\n", + " \"effect\": \"fade:3000\"\n", + " },\n", + " {\n", + " \"effect\": \"fade:-3000\"\n", + " }\n", + "]\n", + "```\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'model_transformation': [{'overlay': {'url': 'https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png'}}, {'flags': 'layer_apply'}, {'effect': 'blur:100'}, {'effect': 'fade:3000'}, {'effect': 'fade:-3000'}]}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'base64': None, 'meta_data': {'api_key': '518455844981529', 'asset_folder': '', 'asset_id': '069d70c84e88ee9293a1d753b3ca3898', 'audio': {'bit_rate': '128290', 'channel_layout': 'stereo', 'channels': 2, 'codec': 'aac', 'frequency': 44100}, 'bit_rate': 644662, 'bytes': 1212449, 'created_at': '2024-11-20T08:55:44Z', 'display_name': 'video_test2', 'duration': 15.046009, 'etag': '876869c16d0bfa291d55796e5cb1bc00', 'format': 'mp4', 'frame_rate': 24.0, 'height': 720, 'is_audio': False, 'nb_frames': 361, 'original_filename': 'ForBiggerMeltdowns', 'overwritten': True, 'pages': 0, 'placeholder': False, 'playback_url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/sp_auto/v1732200207/video_test2.m3u8', 'resource_type': 'video', 'rotation': 0, 'signature': '7df5ccaaf5cba28b4d62d2ffc59793f29dfea326', 'tags': [], 'type': 'upload', 'url': 'http://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200207/video_test2.mp4', 'version': 1732200207, 'version_id': 'fdfd560e5e06e3327777a5355b2cc682', 'video': {'bit_rate': '509638', 'codec': 'h264', 'level': 31, 'pix_format': 'yuv420p', 'profile': 'High', 'time_base': '1/12288'}, 'width': 1280}, 'public_id': 'video_test2', 'url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200207/video_test2.mp4'}\n", + "--------------------------------------------------\n", + "Transition type: finish\n", + "Transition output: {'transformed_video_url': 'https://res.cloudinary.com/dpnjjk8mb/video/upload/v1732200207/video_test2.mp4'}\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)\n", + "\n", + "if transitions[0].type == \"finish\":\n", + " transformed_video_url = transitions[0].output['transformed_video_url']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Video Before Transformation" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import Video\n", + "\n", + "Video(input_video_url)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Video After Transformation" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import Video\n", + "\n", + "Video(transformed_video_url)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/06-Designing_Multi-Step_Tasks.py b/cookbooks/06-Designing_Multi-Step_Tasks.py deleted file mode 100644 index 395f409cf..000000000 --- a/cookbooks/06-Designing_Multi-Step_Tasks.py +++ /dev/null @@ -1,138 +0,0 @@ -import uuid -import yaml -from julep import Client - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an "agent" -name = "Multi-Step Task Agent" -about = "An agent capable of executing complex multi-step tasks." -default_settings = { - "temperature": 0.7, - "top_p": 1, - "min_p": 0.01, - "presence_penalty": 0, - "frequency_penalty": 0, - "length_penalty": 1.0, - "max_tokens": 150, -} - -# Create the agent -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name=name, - about=about, - model="gpt-4-turbo", -) - -# Add a web search tool to the agent -client.agents.tools.create( - agent_id=AGENT_UUID, - name="web_search", - description="Search the web for information.", - integration={ - "provider": "brave", - "method": "search", - "setup": {"api_key": "your_brave_api_key"}, - }, -) - -# Defining a Task with various step types -task_def = yaml.safe_load(""" -name: Multi-Step Task Demonstration - -input_schema: - type: object - properties: - topic: - type: string - description: The topic to research and summarize. - -tools: -- name: web_search - type: integration - integration: - provider: brave - setup: - api_key: "your_api_key" - -main: -# Step 1: Prompt - Initial research question -- prompt: - - role: system - content: "You are a research assistant. Your task is to formulate three specific research questions about the given topic: {{inputs[0].topic}}" - unwrap: true - -# Step 2: Tool Call - Web search for each question -- foreach: - in: "_.split('\n')" - do: - tool: web_search - arguments: - query: _ - -# Step 3: Evaluate - Extract relevant information -- evaluate: - relevant_info: "[output for output in _]" - -# Step 4: Conditional Logic - Check if enough information is gathered -- if: "len(_.relevant_info) >= 3" - then: - prompt: - - role: system - content: "Summarize the following information about {{inputs[0].topic}}:\n{{_.relevant_info}}" - unwrap: true - else: - prompt: - - role: system - content: "Not enough information gathered. Please provide a brief overview of {{inputs[0].topic}} based on your knowledge." - unwrap: true - -# Step 5: Log - Record the summary -- log: "Summary for {{inputs[0].topic}}: {{_}}" - -# Step 6: Return - Final output -- return: - summary: "_" - topic: "inputs[0].topic" - -""") - -# Creating/Updating a task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Creating an Execution -execution = client.executions.create( - task_id=TASK_UUID, - input={ - "topic": "Artificial Intelligence in Healthcare" - } -) - -print(f"Execution ID: {execution.id}") - -# Getting the execution details -execution = client.executions.get(execution.id) -print("Execution Output:") -print(execution.output) - -# Listing all the steps of a defined task -transitions = client.executions.transitions.list(execution_id=execution.id).items -print("Execution Steps:") -for transition in transitions: - print(transition) - -# Streaming the execution steps -print("Streaming Execution Steps:") -for transition in client.executions.transitions.stream(execution_id=execution.id): - print(transition) \ No newline at end of file diff --git a/cookbooks/06-browser-use.ipynb b/cookbooks/06-browser-use.ipynb new file mode 100644 index 000000000..0ca091a1d --- /dev/null +++ b/cookbooks/06-browser-use.ipynb @@ -0,0 +1,763 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task: Browser Use Assistant\n", + "\n", + "### Overview\n", + "\n", + "The Browser Use Assistant is an AI agent that can interact with a web browser to perform tasks on behalf of users. It uses a headless Chrome browser through BrowserBase integration, allowing it to navigate websites, click elements, and type text, all while providing visual feedback through screenshots.\n", + "\n", + "### Task Flow\n", + "\n", + "1. **Session Initialization**\n", + " - Create a Julep session for the AI agent\n", + " - Initialize a BrowserBase browser session\n", + " - Set up connection URLs and debugger access\n", + "\n", + "2. **Browser Setup**\n", + " - Configure browser viewport (1024x768)\n", + " - Initialize with Google homepage\n", + " - Establish CDP (Chrome DevTools Protocol) connection\n", + "\n", + "3. **Interactive Loop**\n", + " - Agent receives user goal\n", + " - Agent plans and executes browser actions\n", + " - Takes screenshots for visual confirmation\n", + " - Evaluates progress towards goal\n", + "\n", + "### Key Features\n", + "\n", + "- **Browser Automation**: Performs web interactions like navigation, clicking, and typing\n", + "- **Visual Feedback**: Captures screenshots to verify actions and understand page state\n", + "- **Goal-Oriented**: Continues executing actions until the user's goal is achieved\n", + "- **Secure Sessions**: Uses BrowserBase for isolated browser instances\n", + "\n", + "### Tools Integration\n", + "\n", + "```yaml\n", + "tools:\n", + "- BrowserBase Tools\n", + " - create_browserbase_session\n", + " - get_cdp_url\n", + " - get_session_view_urls\n", + " - perform_browser_action\n", + "- Julep Tools\n", + " - create_julep_session\n", + " - session_chat\n", + "```\n", + "\n", + "### System Capabilities\n", + "\n", + "```plaintext\n", + "* Headless Chrome browser interaction\n", + "* Browser action execution\n", + "* Screenshot-based visual feedback\n", + "* Single-tab operation\n", + "* Direct browser interaction (no UI controls)\n", + "* Text input and navigation\n", + "* Click coordination\n", + "```\n", + "\n", + "### Flow Diagram\n", + "\n", + "```plaintext\n", + "+-------------------+ +----------------------+ +------------------+\n", + "| Session Setup | | Browser Actions | | Goal Check |\n", + "| - Julep Session | --> | - Navigation | --> | - Evaluate |\n", + "| - Browser Session| | - Clicking | | - Continue/End |\n", + "+-------------------+ | - Typing | +------------------+\n", + " | - Screenshots |\n", + " +----------------------+\n", + " ↑\n", + " |\n", + " ↓\n", + " +----------------------+\n", + " | Agent Response |\n", + " | - Plan Actions |\n", + " | - Process Results |\n", + " +----------------------+\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Installing the Julep Client" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mWARNING: Ignoring invalid distribution ~ulep (/Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages)\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Ignoring invalid distribution ~ulep (/Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages)\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33mWARNING: Ignoring invalid distribution ~ulep (/Users/hamadasalhab/Documents/repos/julep-ai/julep/agents-api/.venv/lib/python3.12/site-packages)\u001b[0m\u001b[33m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "!pip install julep -U --quiet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### NOTE:\n", + "\n", + "- UUIDs are generated for both the agent and task to uniquely identify them within the system.\n", + "- Once created, these UUIDs should remain unchanged for simplicity.\n", + "- Altering a UUID will result in the system treating it as a new agent or task.\n", + "- If a UUID is changed, the original agent or task will continue to exist in the system alongside the new one." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Global UUID is generated for agent and task\n", + "import uuid\n", + "\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a Julep client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Defining the agent\n", + "name = \"Browser Use Assistant\"\n", + "about = \"an assistant that can interact with a web browser to perform tasks on behalf of users.\"\n", + "default_settings = {\n", + " \"temperature\": 0.7,\n", + " \"top_p\": 1,\n", + " \"min_p\": 0.01,\n", + " \"presence_penalty\": 0,\n", + " \"frequency_penalty\": 0,\n", + " \"length_penalty\": 1.0,\n", + " \"max_tokens\": 150,\n", + "}\n", + "\n", + "# Create the agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=name,\n", + " about=about,\n", + " model=\"claude-3.5-sonnet-20241022\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Adding the tools to the agent\n", + "try:\n", + " client.agents.tools.create(\n", + " agent_id=AGENT_UUID,\n", + " **{\n", + " \"name\": \"computer\",\n", + " \"type\": \"computer_20241022\",\n", + " \"computer_20241022\": {\n", + " \"display_height_px\": 768,\n", + " \"display_width_px\": 1024,\n", + " \"display_number\": 1,\n", + " },\n", + " }\n", + " )\n", + "except Exception as e:\n", + " print(\"Already added\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "# please replace the DEMO_API_KEY and DEMO_PROJECT_ID with your own API Key and Project ID of BrowserBase\n", + "\n", + "# Defining the task\n", + "task_def = yaml.safe_load('''\n", + "name: computer-use-task\n", + "\n", + "\n", + "########################################################\n", + "####################### TOOLS ##########################\n", + "########################################################\n", + "\n", + "tools:\n", + "- name: create_browserbase_session\n", + " type: integration\n", + " integration:\n", + " provider: browserbase\n", + " method: create_session\n", + " setup:\n", + " api_key: \"DEMO_API_KEY\"\n", + " project_id: \"DEMO_PROJECT_ID\"\n", + "\n", + "- name: get_cdp_url\n", + " type: integration\n", + " integration:\n", + " provider: browserbase\n", + " method: get_connect_url\n", + " setup:\n", + " api_key: \"DEMO_API_KEY\"\n", + " project_id: \"DEMO_PROJECT_ID\"\n", + "\n", + "- name: get_session_view_urls\n", + " type: integration\n", + " integration:\n", + " provider: browserbase\n", + " method: get_live_urls\n", + " setup:\n", + " api_key: \"DEMO_API_KEY\"\n", + " project_id: \"DEMO_PROJECT_ID\"\n", + "\n", + "- name: perform_browser_action\n", + " type: integration\n", + " integration:\n", + " provider: remote_browser\n", + " method: perform_action\n", + " setup:\n", + " width: 1024\n", + " height: 768\n", + "\n", + "- name: create_julep_session\n", + " type: system\n", + " system:\n", + " resource: session\n", + " operation: create\n", + "\n", + "- name: session_chat\n", + " type: system\n", + " system:\n", + " resource: session\n", + " operation: chat\n", + "\n", + "########################################################\n", + "################### INPUT SCHEMA #######################\n", + "########################################################\n", + "\n", + "input_schema:\n", + " type: object\n", + " properties:\n", + " goal:\n", + " type: string\n", + " required:\n", + " - goal\n", + "\n", + "########################################################\n", + "################### MAIN WORKFLOW ######################\n", + "########################################################\n", + "\n", + "main:\n", + "\n", + "# Step #0\n", + "- tool: create_julep_session\n", + " arguments:\n", + " agent: str(agent.id)\n", + " situation: \"''\"\n", + " recall: 'False'\n", + "\n", + "# Step #1\n", + "- evaluate:\n", + " julep_session_id: _.id\n", + "\n", + "# Step #2\n", + "- tool: create_browserbase_session\n", + " arguments:\n", + " project_id: \"'c35ee022-883e-4070-9f3c-89607393214b'\"\n", + "\n", + "# Step #3\n", + "- evaluate:\n", + " browser_session_id: _.id\n", + "\n", + "# Step #4\n", + "- tool: get_session_view_urls\n", + " arguments:\n", + " id: _.browser_session_id\n", + "\n", + "# Step #5\n", + "- evaluate:\n", + " debugger_url: _.urls.debuggerUrl\n", + "\n", + "# Step #6\n", + "- tool: get_cdp_url\n", + " arguments:\n", + " id: outputs[3].browser_session_id\n", + "\n", + "# Step #7\n", + "- evaluate:\n", + " connect_url: _.url\n", + "\n", + "# Step #8\n", + "# Navigate to google to avoid sending a blank \n", + "# screenshot when computer use starts\n", + "- tool: perform_browser_action\n", + " arguments:\n", + " connect_url: _.connect_url\n", + " action: \"'navigate'\"\n", + " text: \"'https://www.google.com'\"\n", + "\n", + "# Step #9\n", + "- workflow: run_browser\n", + " arguments:\n", + " julep_session_id: outputs[1].julep_session_id\n", + " cdp_url: outputs[7].connect_url\n", + " messages:\n", + " - role: \"'user'\"\n", + " content: |-\n", + " \"\"\"\n", + " \n", + " * You are utilising a headless chrome browser to interact with the internet.\n", + " * You can use the computer tool to interact with the browser.\n", + " * You have access to only the browser.\n", + " * You are already inside the browser.\n", + " * You can't open new tabs or windows.\n", + " * For now, rely on screenshots as the only way to see the browser.\n", + " * You can't don't have access to the browser's UI.\n", + " * YOU CANNOT WRITE TO THE SEARCH BAR OF THE BROWSER.\n", + " \n", + " \n", + " *\"\"\" + inputs[0].goal + NEWLINE + \"\"\n", + " workflow_label: \"'run_browser'\" # <----- REMOVE THIS\n", + "\n", + "\n", + "########################################################\n", + "################# RUN BROWSER WORKFLOW #################\n", + "########################################################\n", + "\n", + "run_browser:\n", + "\n", + "- tool: session_chat\n", + " arguments:\n", + " session_id: _.julep_session_id\n", + " messages: _.messages\n", + " recall: 'False'\n", + "\n", + "# Evaluate the response from the agent\n", + "- evaluate:\n", + " content: _.choices[0].message.content\n", + " tool_calls: \"[ \\\n", + " { \\\n", + " 'tool_call_id': tool_call.id, \\\n", + " 'action': load_json(tool_call.function.arguments)['action'], \\\n", + " 'text': load_json(tool_call.function.arguments).get('text'), \\\n", + " 'coordinate': load_json(tool_call.function.arguments).get('coordinate') \\\n", + " } \\\n", + " for tool_call in _.choices[0].message.tool_calls or [] if tool_call.type == 'function']\"\n", + "\n", + "\n", + "# Perform the actions requested by the agent\n", + "- foreach:\n", + " in: _.tool_calls\n", + " do:\n", + " tool: perform_browser_action\n", + " arguments:\n", + " connect_url: inputs[0].cdp_url\n", + " action: _.action if not (str(_.get('text', '')).startswith('http') and _.action == 'type') else 'navigate'\n", + " text: _.get('text')\n", + " coordinate: _.get('coordinate')\n", + "\n", + "# ----------------------------------------------------------------\n", + "# Convert the result of the actions into a chat message\n", + "# ----------------------------------------------------------------\n", + "\n", + "# Handle image content part\n", + "- evaluate:\n", + " contents: \"[\\\n", + " { \\\n", + " 'type': 'image_url', \\\n", + " 'image_url': { \\\n", + " 'url': result['base64_image'], \\\n", + " } \\\n", + " } if result['base64_image'] is not None else \\\n", + " { \\\n", + " 'type': 'text', \\\n", + " 'text': result['output'] if result['output'] is not None else 'done' \\\n", + " } \\\n", + " for result in _]\"\n", + "\n", + "- evaluate:\n", + " messages: \"[{'content': [_.contents[i]], 'role': 'tool', 'name': 'computer', 'tool_call_id': outputs[1].tool_calls[i].tool_call_id} for i in range(len(_.contents))]\"\n", + "# ----------------------------------------------------------------\n", + "# Check if the goal is achieved and recursively run the browser\n", + "# ----------------------------------------------------------------\n", + "\n", + "- workflow: check_goal_status\n", + " arguments:\n", + " messages: _.messages\n", + " julep_session_id: inputs[0].julep_session_id\n", + " cdp_url: inputs[0].cdp_url\n", + " workflow_label: \"'check_goal_status'\" \n", + "\n", + "\n", + "########################################################\n", + "############## CHECK GOAL STATUS WORKFLOW ##############\n", + "########################################################\n", + "\n", + "check_goal_status:\n", + "- if: len(_.messages) > 0\n", + " then:\n", + " workflow: run_browser\n", + " arguments:\n", + " messages: _.messages\n", + " julep_session_id: _.julep_session_id\n", + " cdp_url: _.cdp_url\n", + " workflow_label: \"'run_browser'\" \n", + "''')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "\n", + "- The `unwrap: True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# creating the task object\n", + "task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def,\n", + " extra_body={\n", + " \"run_browser\": task_def[\"run_browser\"],\n", + " \"check_goal_status\": task_def[\"check_goal_status\"],\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An execution is a single run of a task. It is a way to run a task with a specific set of inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started an execution. Execution ID: 513a0e52-2866-4546-883f-6848080aff8f\n" + ] + } + ], + "source": [ + "execution = client.executions.create(\n", + " task_id=task.id,\n", + " input={\n", + " \"agent_id\": str(AGENT_UUID),\n", + " \"goal\": \"Navigate to JulepAI's Github repository and tell me the number of stars it has. Remember bro, the link for julep's repository is https://github.com/julep-ai/julep\",\n", + " }\n", + ")\n", + "\n", + "print(\"Started an execution. Execution ID:\", execution.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Checking execution details and output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pprint\n", + "import time\n", + "\n", + "def reformat_output(output):\n", + " if isinstance(output, dict):\n", + " for key, value in output.items():\n", + " if (key == \"base64_image\" or key == \"url\") and value is not None:\n", + " output[key] = value[:20] + '...'\n", + " else:\n", + " output[key] = reformat_output(value)\n", + " elif isinstance(output, list):\n", + " for i, item in enumerate(output):\n", + " output[i] = reformat_output(item)\n", + " return output\n", + "\n", + "counter = 0\n", + "while counter < 70:\n", + " transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + " for transition in reversed(transitions):\n", + " print(transition.type)\n", + " output = transition.output\n", + " pprint.pprint(reformat_output(output))\n", + " print(\"----\"*100)\n", + " counter += 1\n", + " time.sleep(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition type: init\n", + "Transition output: {'agent_id': 'f1f7e2d1-cf48-4690-a14f-1886fcbdee95', 'goal': \"Navigate to JulepAI's Github repository and tell me the number of stars it has. Remember bro, the link for julep's repository is https://github.com/julep-ai/julep\"}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'auto_run_tools': False, 'context_overflow': None, 'created_at': '2024-11-22T07:32:11.893049Z', 'id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'kind': None, 'metadata': {}, 'render_templates': True, 'situation': '', 'summary': None, 'token_budget': None, 'updated_at': '2024-11-22T07:32:11Z'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'avgCpuUsage': None, 'contextId': None, 'createdAt': '2024-11-22T07:32:14.722022+00:00', 'endedAt': None, 'expiresAt': '2024-11-22T07:47:14.612+00:00', 'id': '4cfd1fca-7939-4d75-b563-81f1c19458f2', 'keepAlive': None, 'memoryUsage': None, 'projectId': 'c35ee022-883e-4070-9f3c-89607393214b', 'proxyBytes': 0, 'startedAt': '2024-11-22T07:32:14.612+00:00', 'status': 'RUNNING'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'browser_session_id': '4cfd1fca-7939-4d75-b563-81f1c19458f2'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'urls': {'debuggerFullscreenUrl': 'https://www.browserbase.com/devtools-fullscreen/inspector.html?wss=connect.browserbase.com/debug/4cfd1fca-7939-4d75-b563-81f1c19458f2/devtools/page/2DD1378323F30172015B0A5BE297DE18?debug=true', 'debuggerUrl': 'https://www.browserbase.com/devtools/inspector.html?wss=connect.browserbase.com/debug/4cfd1fca-7939-4d75-b563-81f1c19458f2/devtools/page/2DD1378323F30172015B0A5BE297DE18?debug=true', 'wsUrl': 'wss://connect.browserbase.com/debug/4cfd1fca-7939-4d75-b563-81f1c19458f2/devtools/browser/fc2f50f6-0107-4fde-9cb2-4eef8cf6eba6'}}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'debugger_url': 'https://www.browserbase.com/devtools/inspector.html?wss=connect.browserbase.com/debug/4cfd1fca-7939-4d75-b563-81f1c19458f2/devtools/page/2DD1378323F30172015B0A5BE297DE18?debug=true'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'connect_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'base64_image': None, 'error': None, 'output': 'https://www.google.com', 'system': None}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': \"\\n\\n* You are utilising a headless chrome browser to interact with the internet.\\n* You can use the computer tool to interact with the browser.\\n* You have access to only the browser.\\n* You are already inside the browser.\\n* You can't open new tabs or windows.\\n* For now, rely on screenshots as the only way to see the browser.\\n* You can't don't have access to the browser's UI.\\n* YOU CANNOT WRITE TO THE SEARCH BAR OF THE BROWSER.\\n\\n\\n*Navigate to JulepAI's Github repository and tell me the number of stars it has. Remember bro, the link for julep's repository is https://github.com/julep-ai/julep\\n\", 'role': 'user'}], 'workflow_label': 'run_browser'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': \"\\n\\n* You are utilising a headless chrome browser to interact with the internet.\\n* You can use the computer tool to interact with the browser.\\n* You have access to only the browser.\\n* You are already inside the browser.\\n* You can't open new tabs or windows.\\n* For now, rely on screenshots as the only way to see the browser.\\n* You can't don't have access to the browser's UI.\\n* YOU CANNOT WRITE TO THE SEARCH BAR OF THE BROWSER.\\n\\n\\n*Navigate to JulepAI's Github repository and tell me the number of stars it has. Remember bro, the link for julep's repository is https://github.com/julep-ai/julep\\n\", 'role': 'user'}], 'workflow_label': 'run_browser'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'choices': [{'finish_reason': 'tool_calls', 'index': 0, 'logprobs': None, 'message': {'content': \"I'll help you navigate to JulepAI's Github repository and check its stars. Let me break this down into steps:\\n\\n1. First, let me see what's currently on the screen.\", 'created_at': None, 'id': None, 'name': None, 'role': 'assistant', 'tool_call_id': None, 'tool_calls': [{'api_call': None, 'bash_20241022': None, 'computer_20241022': None, 'function': {'arguments': '{\"action\": \"screenshot\"}', 'name': 'computer'}, 'id': 'toolu_01SF7tWSGgctWmwTWqET1zbV', 'integration': None, 'system': None, 'text_editor_20241022': None, 'type': 'function'}]}, 'tool_calls': None}], 'created_at': '2024-11-22T07:32:32.153810Z', 'docs': [], 'id': 'e5df81d3-6522-4617-b217-bf98d7892418', 'jobs': [], 'usage': {'completion_tokens': 94, 'prompt_tokens': 1335, 'total_tokens': 1429}}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'content': \"I'll help you navigate to JulepAI's Github repository and check its stars. Let me break this down into steps:\\n\\n1. First, let me see what's currently on the screen.\", 'tool_calls': [{'action': 'screenshot', 'coordinate': None, 'text': None, 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}]}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: {'action': 'screenshot', 'coordinate': None, 'text': None, 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}\n", + "--------------------------------------------------\n", + "Transition type: finish_branch\n", + "Transition output: {'base64_image': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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', 'error': None, 'output': None, 'system': 'take_screenshot'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: [{'base64_image': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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', 'error': None, 'output': None, 'system': 'take_screenshot'}]\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'contents': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAG4J6QkFDcdQAAAAAAAFcYPQAAAAAAADAAAgAAAAAAAAyAAAAAAAAAAAMgAAAAAAAAwAAIAAAAAAAAMAACAAAAAAAADIAAAAAAAAAAAyAAAAAAAADAAAgAAAAAAAAwAAIAAAAAAAAMgAAAAAAAAAADIAAAAAAAAMAACAAAAAAAADAAAgAAAAAAAAzAvbgrAAAoWiOWPqkGNT0U6Fla1fyaqIbfzSrlWVFmt5y/8k0yXeUaAgAAoDjQAwAAbjALthzRsh1ndMFyUlui5mvm6Tc1+9x4hSefkVPOSyrT6XQqLi5Odru9iGubnc1m06RJkxQaGprrPGlpaYUqc+XKlVqxYkWB5j148KC+//77QpVfFOLj4zVp0iTFxsZekfKjoqL05Zdf6s0331R8fHyBl4uMjNSkSZOUlJQkSbLb7XI6L+04ut4U9ji7VDabrcDzXq06AQBuTAQAAHAD2nM4QUfPJrt+P285pj/Oj9eWqD8LVY7T6dSUKVPUvn17tW/fXm3atNE777wjq9VaZHW12+06ePCgUlJSXL9PnjxZFy5cyHH+w4cPq3Xr1rlOz8mqVau0fPnyAs176NAh/fDDDwUuu6jEx8dr8uTJiouLK/KynU6nHn/8cW3ZskWVKlWSh4dHgZeNiIjQ5MmTZbFYJEmjRo3S22+/XeR1lLIfC1dTWFiYzp496/p95syZ6t+//xVf78SJE/Xkk08WaN5LOfYBAMiMWwAA4Abj4+EtS1qKVm2Llq+3WRXLeEuSUuzJ2hI1TxZ7gjqVfahAZU2YMEGzZ8/W2LFj1bx5cx09elTjxo1TdHS0PvnkkyKpb3x8vAYOHKjffvtN9evXz3f+6tWra+LEiSpXrlyRrN8IwsLCdOTIEc2fP1/VqlW7rLKGDx9eqAChMAp7LBSlb775RhcuXNDnn38uSerRo4caNmx4xdd7zz33qGvXrgWal2MfAHC5CAAA4AZTq0xV7Q09JGuqQ2t3Ruuu28rJ28vsmr4ndrX83YN1S3BPmUy5dwSLiorSjz/+qLffflu33367JKlUqVIaP368HnjgAR08eFD169fX7t27lZiYqHbt2klKv9o8Z84cdejQQWXLltXq1atVsWJFnThxQklJSVmuqh49elSbNm2SlH6VPjU1VfXq1ZOU3tV5/vz5Onv2rJo0aeIqPykpSaGhobJarfLw8NDcuXPVqlUrrV+/XtWqVVPr1q2VmpqqhQsXKiIiQs2bN893n61du1b79+9X9erV5XA4sk0/evSoVq1aJQ8PD3Xt2lWVK1d2TYuPj9eSJUsUFRWlOnXqqHPnzjKZ0sdVsNlsWrFihU6cOKHy5curR48e8vHxcS17+PBhrV69WsHBwWrSpEm29UZHR2vx4sWKj49XmzZt1KxZs1y3ISIiQkuXLlV8fLwaN26sDh06SJLOnj2rhQsXSpLWrFmjCxcuqHXr1tmWz2s7MgsLC5O3t7cqV66sPXv2yGKxKCgoSJs2bZLZbFbv3r3l6+urhQsXKjw8XE2bNlWbNm2ylLF161b9/fff8vX1Vbdu3VSuXLkcj4WmTZvmu/8vllPZ+W3j/PnzdezYMcXHx2v27Nnq06ePoqKidOHChSzvy8GDB7Vu3TqZTCZ17NhRderUkZTec2Hu3Lnq1KmTNmzYkO2YtVgsWrFihXr06JEtPImKilJERITruJekdevWae/evQoMDFT37t1VunRpSVmPfW9vb61evVpVq1ZVeHi4du7cqUqVKumOO+7IMaDJ73MqSXv27NGWLVvk5eWljh07qmrVqrnuZwDA9YlbAADgBtOpVivX/yNi0rRoY2S2ebZGz1dY8pk8y9mxY4fsdrur8Z+hSZMmCgkJ0datWyVJixcv1s8//+yabrfbNW7cOB07dkyS9PXXX2vYsGH66quvdPTo0SxlRUdHu147efKkwsPDXdPGjRvnakwNHz5cc+bMkSSdP39e48aNU2Jiomw2m8aNG6chQ4ZoyZIlOn/+vCTp6aef1sSJExUeHq6JEydqzZo1uW7n1KlT9fzzz+vcuXNatWqV6wpwhgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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'}, 'type': 'image_url'}]}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'messages': [{'content': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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'}, 'type': 'image_url'}], 'name': 'computer', 'role': 'tool', 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}]}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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'}, 'type': 'image_url'}], 'name': 'computer', 'role': 'tool', 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}], 'workflow_label': 'check_goal_status'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20cOFCLVq0SFOnTtXgwYP13HPPadeuXUpOTlZcXJzr8+Lu7q59+/ZJkp566imdPn1ac+bMUWRkpP755x/X5yhDRoghpXfd79q1q6traHBwsObNm+f6PEi5v/eZ1ahRQxUrVtS2bdtUrVo12e12DR48WKtXr1b37t21detWV4iQ376TpEqVKmVrJOzcuVNTpkzRmDFj1Lp1a9fr1/JxBBjJww8/LCm9YeXp6amOHTtmuf8/p891hs6dO+vtt9/Wrl271KRJE61Zs0afffaZpLy/1/bv3y+r1Zrl796wYcMUFxcns9msnj17atmyZbrvvvu0evVqtW3bNsv327Vmw4YNKlu2bJYeDn5+fjp48KArAMj8HS6l/w0oVaqUpPTzpQoVKmT73rbZbEpISFBQUJAaNWqkTz75RPfdd5/atWunSZMmXYUty1vp0qX13Xffadu2bVqwYIHGjh2r4OBgTZgwQbVr185zv2T+W1IYuZ372Ww27d+/P8v5ysXnfnmd2wBXGgEArgqr1apFixapbNmyri+5qKgo7d27V6GhoQoJCVFcXFy2K6aZ3XTTTSpZsqTWr1+v/fv3q3PnzvLx8XF1pzp69GiWrl89evS45EfRlStXTrNmzdLvv/+uL774QqdOndJjjz2WJb3Nj9PpVHx8vHx9fa/ZQXlq166d5R5yu92uiRMnFigA6Nixo6ZPn64//vhDL730kux2u8aMGaNbb721QOvO6J6ekJAgq9WqDRs2uKZVrFjRdcV65MiRatSokZYtW6bJkyerSpUq+uCDD1S1atXLrsOV1KVLF9f9+hkyblmRpNjY2GwnkYGBgbLZbEpKSlJISIhq1KihLVu2aNWqVRo0aJAsFosWLVqkUqVKqU6dOq6TuZwEBwfrwQcf1IMPPqiNGzfqqaee0k033aTk5GS5u7try5YtWebPuOowc+ZMffXVV+rbt6/Kly/v6nKfm/j4+Gyf25CQEEnKdfTl3B6j1KFDB23evFnHjx/Xbbfdpi5dumjSpEmKjo7WkSNHXGMH5LfvcrNp0yZVrVpVS5cuVb9+/VzjFFzLxxFgJBMnTlT9+vXl5uam4ODgLGOJ5Mfb21tdu3bV0qVLZbFY5O/v7xpTJK/vtfj4ePn5+bl6GUjpDcOMoKF379768ccfFRkZqdWrV2v48OFFtLVXRkJCgtLS0rL8TW3Xrl2R3KOf8d09bdo0/fHHH5o7d67GjRunzp0766233nKN0VScWrRooRYtWmjUqFF66aWX9Prrr+vXX3+9ovslM6fTqaSkJNnt9jzPafM6twGuNAIAXBUrV66UyWTSgw8+mOX1xMRE/fnnnxo2bJhCQkK0adOmPMvp3bu3li9frgMHDujll1+WJJUqVUo+Pj7q16+fevTokW2ZQ4cOFbq+x48fl8Vi0YgRIzRixAjt3LlTjzzyiDp27JgtOc9LxYoVlZiYmOutA9eagIAAV6PNw8PD1fsiJ3///bdKly6tsWPHSpK++uorvfLKK1n+uBZEpUqV5O/vn+O4CDabTZs2bVKbNm3UvXt3paam6rnnntOECRP06aefFlkdikOVKlWyPZf+yJEjCggIcPWS6dChg1asWKH9+/erbdu2SktL0zvvvKMyZcrk2tXy/fffV2JiYpbxG9q2batKlSrp0KFDatOmjWw2m0aPHp3jMfnVV1/ppZdecg1S6XQ69c8//+S6HRUrVszy6CRJ+uuvv1SvXr0sgyEVRIcOHTR+/Hj5+/vr+eefV/ny5VWlShVNmTJFDRs2dO2Xguy7nDz44IN69NFHdffdd2vatGl6/PHHJRXdsQzg8gQEBFzW38revXvr1VdfVUpKinr27OkKEPL6XqtYsaLi4+OzhJlnz57ViRMn1KFDB9WvX181atTQlClTFBERodtuu+3yNvIKq1SpkkJCQvT6669nm1YUjwiMiorSoUOHXAFzZGSkBg4c6LoHvjicO3dODz/8sCZNmuQ6R/Pz81OvXr30xhtvyOl05rlfilpgYKD8/f118uTJHG8fyO/cBrjSGAQQV8XcuXN1xx13aMCAAdl+5s2bJ6fTqW7duuns2bOaNWuW7Ha7jhw5oo0bN2YpJyMASExMdHXhNZlM6tevn7788kudO3dOUvpovi+//HKuVxolydfXVxERETnOs3v3bj311FM6deqUJLmuDHh5eRVqu+vVq6caNWros88+U0pKimJjY3McAK24pKSkKCYmRlFRUdqyZYt++ukndezYUZJUrVo1paWluQZe3LJliw4ePOha9rffftOYMWMUHx8vKX0fFXb/SOk9NUJDQ/Xtt9/K4XDIarXqrbfe0pYtW+Tu7q533nlHX3/9tSuMMJvNrqsMRVWH4tCrVy+FhYVp5syZcjgcCg0N1VdffaV7773XNU+HDh20dOlS3XLLLfLx8VFAQICaNWumP//8M9cAoH379lq8eLH+/PNPpaamyuFw/H979xISVRuAcfw5XiqRNBqTamOQQldQRyd11BgvyWRmLqJFgUV0wy6W4iahQixtIKIoSSo3QSlEERRBC6GCpJ27LnQBKUPHmZIxtXHyW0gntDTryyzm/1vO9Zx33jnn5Xlvunv3rjo6OpSYmKhly5ZpyZIlqq2tVX9/v4aHh3X16lVdunRJ0kgdb29v19DQkF6+fKmWlhYFAoFxz6O4uFi3b982FxG8f/++KisrJwyPxpOamqp3797pzZs3Zs9dbm6uWlpaRvXGT6bsvic6OlqxsbGqra3V+fPnzQWX/uV6BOArm82mkJAQ3bp1a9R2thNd15YvX674+Hg1NDQoEAjI5/OppqbGXPBWGml7NDc3y+Fw/HBU1HQrKSnRzZs3zVFeXV1dOnDggDla8v/y+Xzas2ePWltbJY20wab7mrlw4ULNnz9fLpdLHR0dkiS3260bN24oMTFRhmH8sFwiIiLk8XjMehERESFpZGvdX+F0OnX58mW53W75/X5duXLFfO5HbRtgqhEAYMp1dnaqra3tu3vLr127Vp2dnXr8+LGZzNbX1ys1NVUVFRXfJKdxcXFaunSpnE7nqOF65eXlSkpKUlFRkTIzM3Xo0CHl5eVNOHywsLBQTU1NKikp+ea54uJiFRQUaMOGDbLb7dq5c6cqKyu1aNGinzp3wzBUU1OjR48eKS0tTYWFhYqOjv6pYY1T6cKFC8rOzpbD4VB1dbUKCgp08OBBSSO9rDt27FBZWZmsVqtOnz496vwrKioUFham1atXKy0tTc3Nzd/sGjAZCxYs0JkzZ9Tc3Ky0tDRlZmbK6/WaW0S6XC61trYqPT1dGRkZ6unpMaeR/K5jmA4xMTFyuVy6ePGibDabnE6nVqxYoV27dpmvSU5O1uzZs5WTk2M+lpeXp8jISHMBu7GysrJUV1enxsZGpaamymq1qq6uTtXV1bLZbAoNDdWpU6fk8Xhkt9tlt9t1GiJcowAAA4pJREFU/fp1ORwOSdLhw4d1584dJSUladu2bbJarerq6jJXNh5rzZo1WrdunTZu3Kj09HQdOXJE9fX132xLNRmzZs2SzWZTdna2+f/Oz89XIBAYFXhMpuwmkpWVpdLSUlVVVcnr9f7T9QjAV4ZhqLCwUAkJCVq8eLH5+ETXNcMwdPz4cT18+FCrVq1Sdna2IiMjVVlZab7/y8gBp9P5x8/pZ+Xn52v//v0qLy+X3W7X+vXrlZCQ8NtGIcbFxeno0aOqrq5Wenq68vLylJKSMmrL4D/NMAydO3dOMTExKikpUUpKihwOh2bMmKETJ05I+nG55OTk6NmzZ0pOTpbb7TanlJSVlf3S/WDfvn2aOXOmHA6HMjIy5PF4zGOVJm7bAFPN6O3tHb+LFJgGAwMD8vl8v3Sz+vTpk7xer2JiYkYFBON5//69wsLCzH3Fx/L7/fJ4PLJYLOYiZz9raGhIhmHI7XZr3rx5ampq0r1793Tt2rVf+rw/ra+vT36/f9yh1X19ferv75fFYvnfwYbH41F4ePh35+R5vV6FhoZ+d07d7zyGP+3z58/q6upSZGTkb5+L2NPTI8MwzD2jx/L5fBocHDQXf/oiEAiou7tbFotF4eHhk/qu/v5+9fb2KjY29o/9Br+77P7legRgYpO5rn25noxdaPD58+faunWrWltb/9o1fcYaHh5Wd3e3oqOjp6R3/svnR0VF/VU914ODg/J4PJo7d+53z3uicvn48aMGBgZG3TO/vPZnf/dAIKCQkBC53W5FRUXp6dOn2rx5s9ra2kbVr4naNsBUIQAAptju3bu1cuVK5ebm6u3btzp27Ji2b99urnYMAAD+Pn6/X69fv5bL5VJ8fLyqqqqm+5Dwj2hsbNSTJ09UWlqq4eFhnTx5UhaLRWfPnp3uQwMIAICp9uLFCzU0NKi9vV1z5sxRUVGRtmzZopAQZuAAAPC3evXqlTZt2iSr1SqXyzXuaEFgrA8fPqihoUEPHjyQYRjKyMjQ3r176enHX4EAAAAAAACAIEAXJAAAAAAAQYAAAAAAAACAIEAAAAAAAABAECAAAAAAAAAgCBAAAAAAAAAQBAgAAAAAAAAIAgQAAAAAAAAEAQIAAAAAAACCAAEAAAAAAABBgAAAAAAAAIAgQAAAAAAAAEAQIAAAAAAAACAIEAAAAAAAABAECAAAAAAAAAgCBAAAAAAAAASB/wAZC67GWTlxPwAAAABJRU5ErkJggg=='}, 'type': 'image_url'}], 'name': 'computer', 'role': 'tool', 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}], 'workflow_label': 'check_goal_status'}\n", + "--------------------------------------------------\n", + "Transition type: init_branch\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk6RFJWvPXX00fe2xI2q5duw4Ud90AAED+CAAAADAwk8lUqKcBtWrVqvE307+xt27der8kbd++vf7//d9jXhs3bNh9ZWoIAACKCgEAAAAolMaNm9T7eto3Pl26dN0lSfv37av5f//3WMlly5Ztk8TjhQEAuEYRAADADcbsxle7Ubi5mQq9jLvZLKeccjqdhV84k9q1a9f4aurUsn379t0qScePH6889MknqsyZM3uT0+l0XE7ZQGGYZLqkzwIAGBFniQBwgzGb+Wo3CnezudDLFOXxUalSpYpfTPyy2oMPPrjBzc3NERoaWu6Zp5+p98MPP2yw2+22IlsRkAuTTHLIIQ939+KuCgBcFzhLBIAbjMlkkqeHO1fEbmBubunvscl0ae+xt6dnkdWlbNmyZf/38ScNnnxy6Hp3d3dbVFRkyVEvvdhsyuTJG1JTU61FtiLgYibJ5Fa0xzMA3OiISwHgBmQymbgihgJxc3OTl6eHPD09LrmMcmXLBH/44fibg4ICN0yYMKF1XFxciTfeeL1lcnLSphdeeKGlj4+PbxFWGQAAXCJ6AAAAgMvm7+/vP2bMmJZjx47d5O/vn5ScnOzz9ttvtx0zZsy2+Pj4+OKuHwAAIAAAAABFxNvb2+fFF19s9+GHH+4IDg6OS01N9ZwwYUL7UaNG7Y6Ojo4u7voBAGB0BAAAAOCyOJ1Oh81mS0tJSbFYLJaUe+65p8Ho0aN3+fj4pNjtdvPUqVPbP/PMM4cuXLgQVtx1BQDAyLhBFAAAFNjevXsPrVu3LiwxMdERFxdnSkhIMMXFxZkTEhI8EhISPBMSEjyTk5M9k5KSaqWlpblLktPpNM2YMaNNQkLCts8//zy1WrVqlYt7OwAAMCICAAAAUGChoaHxo0aNuiUpKanQA/vNnz+/RVJS0u7PPvvM0qhRozpXon4AACB3BAAAACBHcXFxcZs3bz582223Nfby8vKWpLZt29Zr167doWXLlt0kSd7e3lZ/f/8kb2/vNB8fH6uvr6/Vz8/P6u/vn1aiRInUgIAAW0BAgD0gIMAREBBgKlGihCklJcVcvFsGAIAxEQAAAGBgJpMpx9fj4+Pjx4wZs3vOnDl158+ff6JZs2b1JalEiRIl7rvvvsSVK1fa7Xa7+bbbbts/fvx4X39/fx8fHx8vLy8vf09PT08PDw93d3d3d7PZbJaU80oAAMBVxSCAAAAYkNPpzLVRHhMTEzN69OjdX375ZbuzZ8+W+/3338MzT+/Ro0edxo0bH5Ok/fv3h7i7u7vXqFGjaoUKFcqXLFmypL+/v7+Xl5e32Wx2F41/AACuGQQAAADAJSoqKuqFF144MHXq1HZ2u90sSXPmzKl6+vTpsxnzlC9fvtw999wTKklnzpwp//vvv5/NrTwAAHDtIAAAAACSpPDw8PBnn3320HfffdfW4XC4ubu72yTp0KFDVefPn38s87z9+/evXKVKlfOSNHv27Mrnzp0LLY46AwCAgiMAAADAwJxOp0wmk9v58+cvDB8+/MTPP//c1ul0mmrWrHlm3Lhx68uUKRPtdDpNv/zyS8nY2NiYjOXq1KlTrXfv3kclaf/+/dUXL158LPe1AACAawEBAAAABubm5uY8ffp06NChQ8/MmjWrlSTVqlXrzJQpU8JfeumlNp07dz4sSdu3b6+zevXqQ5mWM993330BQUFB8Q6Hw23GjBkB8fHx8cW1HQAAIH8EAAAAGNjZs2cDhw4dGjtv3rwWklSvXr2T33zzTVTXrl2be3p6et1///0mHx+flJSUFK8ZM2aYrFZrSsayt9xyS51OnTr9I0lbtmyps27dun+KazsAAED+CAAAADCwPXv21FqxYkUzSWrUqNGx6dOnx996663NMqZ37NixbosWLQ5L0sqVK+vt3LnzcMY0b29vn/vvv9/u5eWVmpyc7DNjxgx7Wlpa6tXeBgAAUDAEAAAAQDfddNPh7777LqVNmzZNMr8eGBgYdP/998eaTCZnTExM4C+//BLrdDodGdM7d+5c9+abbz4iScuWLau7e/fuI1e77gAAoGAIAAAAMLiWLVse/O677xzNmzdvmNP0Xr161apfv/4JSZo3b16tI0eOnMyYVrJkyZKDBg2KlKTIyMiSv/76a6Qk59WoNwAAKBwCAAAADKx9+/Z7v/32W48mTZrUy22eSpUqhQwYMOC0JJ08eTLkjz/+OJ15et++fWvUrVv3pCTNnTu3xvHjx0/nUAwAAChmBAAAABiQ0+k0derUaff06dP9GjRoUCu/+e++++4KISEh4ZL022+/VQgPDw/PmBYQEODXoEGDcEk6evRo5Xnz5p28YhUHAACXjAAAAAAD6t+//7avv/46sHbt2jUKMn/Dhg1r3nHHHYclac+ePTWXLFly+Pz58xcmTZq0tk+fPqGLFi1qmjHv77//Xjo2NjbmStUdAABcGpPT6eQ+PQAADCYmJiY6ODi4ZGGWWbly5Y677rqrXkJCgl+tWrXOeHh42A4dOlTV4XC4SZK3t7e1RYsWhx999NHYQYMG3ezr6+t3ZWoPAAAuBQEAAAAokKSkpKSBAwf+s2jRouaZXw8ODo7r0qXLPw8++KCzY8eO9YKCgoKKqYoAACAP3AIA4IZxPtSijesisv2Eh6Vccpl7dsVo8KANRVjL/K1dFaYXhm8vsvIO7o/Tn7POaN1f4UpKtBVZuXl5/cVdWjz/XKGXczikbZujNG/OWe3eeW30IB/+2BZt3RSZ47TQcxYd2BdXqPIyH6dbNkbq5PGkLNOL4phbuypMI4dtK1RdMv/ERKfmOP/OrUl+9957X4q7u7vNZDI5q1WrFjpixIi1ixYtOl29wotNfTxatC6qxn9EuFWPP7hJ7W9acs0cCxeb9OkhffvV0WKtwysjd2rZotDLKuNSP695Kcz7V9TfeQCA3LkXdwUAoKgsmX9O743bq4aNg7K8PuLF+up8e/lLKjMuNk3bNkcVQe1yF3YhRd9+dVQvj20kKf3Eec+uy2/w2O1OPfP4Vq1ZeUHNW5ZSdFSqQs8m67tf26nJTcGXXX5e9u2JVc3aJQq1TEx0qgb2XiO73akatUpo354YVavur+9+ay9vbzdt2xylo4fjdd/D1a9QrdPNmnlKFSr6qt2tZSRJu7ZHq89dlXOcd+HcszqwL1YTJrcocPmZj1OnUzp+NEFVqvnp5zm3KijYo0iOuYhwq/7ekf8xlNtn5rW3mqhF61JZXktNdeilEdv10cSWNW677bZ9zZo1i3v00Ucr1KtXr63ZbHZ/59VVatT00sO2i3349j4lJdn03a/tVKmyb5GVW5SOHUmQn3/Rnkpd/H2Qn727Y1W/UdBlrfNSPq/5yev9u1LfeQCA/BEAALih1KxdQnOXdSruahRKRHiKvpzwT4FP+Avq9xmntG1TpFZu7q7yFbwlSR+9s1/PPrlNq7feXqTrKgpfTvhHpUp76Zd5t0qS0tKc6tNlpb7+8rCeeaGe/t4RrRVLzl/xAGDhn+fU9OZgVwCQl8eH176kdWQ+TlNTHep120r9OP2Ynnkh1yfxXTEF/cx4erpp896eklRhecflFa50vQ7/E6977q+qWnWKtmF6rbtS3wdXW17v342yjQBwPeIWAACGsGjeOd3RcaVSLHZJ0mcfHdSj926Q0ykNuOMvffzeAd3afKnqV/5TI4dtU0qKI8dy1q8JV/f2K1QnZK76dFmVpWvrgDv+0puv7lbzugs0bdIRSdLUiUd0c50Falx9nkaN2CGrNWu5v/x4Ug8NWC+HQ2rZcJHrym9amkNvjPpbjarNU4sGC7N08T19Mkn39lmrWuX/UJfWy7RpfUSOdf3lxxN6+P9quhr/kvR/T9XWgEFVXPvhzKlkPdB/nepVmqtbmy/RrJmnspQxbdIRtW26WA2r/qn/e2CTIiOsWereqtEiNa4+Tx+8uU+dWy3L1p1dkizJdr349A7VrThXrRot0jeTc+4yHROdquCSnq7fPTxMevvDm9S4abBeemaHJow/oG2bI9Wy4SLZ7U5Zku16+bmdalpzvprXXaB33tijtLT0YW2WLAjVoDvX6vEHN6lJjfkF3m9dWi/T2lUX9NUXh/XEQ5tcr+/YGqXb2y1X7Qp/aMh9G13Hx5TPD+uVkTtd83364UE1qzVf9Sv/qSce2qTYmLQctzUzT083Va7qp+goa47Tf/jmuFo3XqS6Fefq7p5rsuzjvX/HqudtK1Wz7Bx1vGWpViw5n215q9WhwYM26t0xe/OtS2bvjtmrF4ZvV/f2K/TY/RtltzvVsuEiHTuSKCn9VoXu7VeobsW5+r8HNikh/r9tTU116LUXdqlpzflqUOVPPftk+mfq4P44tWmyOMt+6dVppZYtylrvAXf8pd07o/XBm/s04I6/JKnQ73dmeS27YO5ZPXrvf7dcnDyepJYNF8lqdSg8LEUtGy7Su2P2qkGVP7Vvd2y2ss+cStKAO/5SnZC56t/jL509neyatmZlmDq1XKa6FedqUN+1Cj1ncU3L6VjJ7fsgs7z2e16ftdCzFj0ycIMaVPlTN9dZoMmfHc5WtiQlJdo0/LEtql/5TzWtOV/j39qX43x57dOc3r8MV+o774Xh2/XJBwdcv//x22kN7L0m3+U3b4hUj1vTv9Nva7FUf60Ik6QCvfcAcD0iAABwQ0lKtGnd6nDXz67t0ZKkO/pUlL+/u774+B8dO5KoLz7+Ry+80lAmU/p90Lt2ROvX+bdqwarO2rY5SpMm/JOt7FMnkvTIwA16bnR97T1xp+5/pLoG9V2r6Kj0+6XPh1q0eUOkJn3bWgMGVdWsmaf07dSjmrOkkzb8fYeOHUnQ5M8OZSnznvuravrMtnJzk9bu6KHmLdO7XR85lKD6DQO1/WAvPTSkpquRabM59eCAdWrToYwOnO6rUW800mP3b1RiQvZ7+08eT1Td+oFZXgsu6amnn68nbx+zbDanHui/To2aBGvX4T76ZFILjRn9t9atTn+8+6yZpzTl88OaPrOdtu7vpaBgT/3fAxslSQf2xenl53bo7Q+baev+XvLzd9ehg/Gy2bIHJ6+/tEuRESnafrC3Zvxxqz776GCO99Q/+ngtLV9yXk8N2aINayPkdEotWpfSbV3L6b1PbtYzL9RX85altHZHD5nNJr3+0i6dPJ6o1Vu7a8HqLlq/JlyfvL//v+Pgr3A1b1lKsxZ1LPB+W7Smqzp0Kqf/G1ZbE79p5Xp9/Zpwffdbe63Z1kN7d8dozq/pQUlcbKorFFm7KkzffnVUi9Z01faDvZSUZNNH7+TceIqPS9OSBaFaNO+cxr+1T7u2R+vBwdmfxrdlY6Q+fHufZvxxq/af6quq1f30xqhdktJ7SPzfgxvVq28lHQq9S29/2EzDH9ui0LP/NTJtNqeGPrJZaWkOjXoj56utdptT0VGprp+MfRIdZdXi+ef03Oj6evvDm+R0SufOJCstzaHUVIeG3LdR3e6ooK37e+me+6vqzKn/gokvJxzSgX1xWrezhzbu7qn9e2I1fcoR1W8YKJNJWrIg/X7zfbtjdXBfnNp2yNrbYuaft6phkyCNeqORZv6Z3iOkMO/3xfJb9sL5zPvMoXNnkuV0pu+/c2eSFRGeol/ndczxavaOrdF6c3z656ByVT89NzR97IXjRxP1xMOb9OYHTbXn+J1q3DRYI/+dltuxktv3QYb89nten7Xnhm5VjVr+2nviTs3881b97919Od4m8sXH/ygq0qqdh3pr4eou+u3nk1ow92yh9mlO71+GK/Wd16Z9Gf3y40nX7/PmnFHzlqXyXN6SbNfD96zXsy/V16Fz/TR0RF09+cgm2e3OAr33AHA9IgAAcEMJu5CiiZ/84/r5fUZ6Q81kkv438RZ99/VRPf1/W/T4U7XVuFmQa7kHB9dQhRAf1axdQsOerZvjCe8vP57QrZ3K6Y4+FeXl5ab7Hq6u2nVLaO7vp13zjHixnlq1La3gkp766dvjenBwDQWX9JTTKQ16qLoWz8s60JbZbJKHZ/pXsbe3m9z+/VZu0ChQ9z9SXd4+ZvW8s6IunE9RSopDm9ZFKDLCqkcfryVLsl2t25VRmbLe2rguPFt94+PSXPcnnz2drBeGb3f9hIelaNO6CEVHWfXS6w3l42vWLa1K6aEhNfX9tGOSpJ++Pa4nn6mjeg0C5OfvrnHvN9XunTE6uD9OC/44q45dyuv2niHy8TVr2LN1c3w/LMl2zf7llIaPrCeHw6nSZbzUvVeIFs3LPuBY42ZB+mtrdwUGeujxBzeqZYOFmvH9CUnpvQHc3U1yczPJ29tNVqtDv/18Uq+/00QlS3mqQoiPXhnbWD98c8xVXv2GgRo6oo7q1Aso8H7z8nKTyWSS2d0kT8///kQ++1J9hVT0UUglH93SspROHEvMVn+73Sm73anz5yzy9XPXl9+01vCROXfpj41J1exfTumP305r7eowVajoI29vc7b5bmlVSjsP91bZct46dSJR9RoE6ujhBEnS5vURSrU69NRzdeXubtKtncvpjXeaKDn5v4bRi09vV0y0VV//2EYeHqYc63LoYLxubb7E9TNm9N+uaQMGVdUdfSoqpJJPlmW2boqUxWLTc6MbKDDIQ917hahBpnEEnn6+nn5fmB68hIdZVLdBgI4dSa93/4FVNP+PM5LSg4CuPSrIv0TWOxI9PdPfB/d/34fCvt+ZFWTZ/Ix7v5kaNwuSt0/296jfPZXVqEmQgoI9NOr1htq0Pn0Qxd9nnFTrdmXU9OaSSrHY9eDgGlq/JlwJ8bZcj5Xcvg8Kst/z+6z9OLuDXnuricIupI/TUKGir44ejs+2PXa7UykWuyLCU1Spiq8WrO6idreWLdQ+vfj9y+xKfef1vLOSoqOs2rktWpZku9auClO/e6rkuby3j1l7jt+pzrdX0KkTSapZu4QSE2wKO//fWBZ5vfcAcD1iDAAAN5Qatfz16/xbc5xWtbqf7uxfWb/9fFKzF92WaxlVq/sp7IIl2+tnTidnuwpUq06ATp74rzFoMv3XyDp7OlmTPzuk6VOOuF4rVdqroJviYjanl2m3OXT2TJKSk2zq0npplnni47J3Nfcv4aHI8PQTWV8/dzVvWUopFrveGPW3hj1bV2dOJ6lqdT+5u/9X51p1SmjVsvTu2GdOJWUZGMy/hLvKVfDWqRNJCrtgUZVq+T/iPexCitLSnBr6yKYsr9/eMyTH+StV8dX7E27Wm+ObafG8c3p55E65uZk06KFqWeYLPZssh0Oqlal+teqUUGxMmuJi0/dFpreiUPstP2Z3N9nt2Z+g26lbeb3wakM9/9Q2xcWmqvddlfXiqw1zLKNKNT99/VMb1+8vP7dTY1/erWk/t8kyX1qaU889uVUH9sWqTr1ARUakuNZ97myyKlb2zbKdGb0Idu+M0ZFD8Tp5PFHtbyubZ+OlQeNALVnbNcdpppwzA50/Z1GlKlmPnczOnUnWsMGb5eYmVa7ip/17Y11XevvfW1VffPyPYmPStHRhqEa+3CDXumUo7Ptd2GXzk1vZF6tY2VdubtKF8xadPZOsTesj1LnVf8dc+QreioxIKdSxklle+z2/z9raVWEa+/LfqlLVT8ElPRUVac3xOB45uoHGv71Pd3ZdpcAgT933cHU98XSdLPPkt08Dgzzy3Za8XMp3nq+fWT16V9SCuWcVHpaiajX8Vbd+gHZui8p1eZNJ+uqLQ/r5uxNq0ChQ7u7paYTd7pT5331c0PceAK4XBAAADOPs6WTNnXVGNWuX0JTPD+n5V3JueIRfSFGp0t7ZXi9fwVvnziZneS30XLJu7VQux3JKl/XSo4/XytZ4vRyly3irTDlvbTvQK995b7qlpNavCVffuyurZClP3f9IdcXFpumNUX9LksqV99H5cxY5nf+d5J4Ptah8SPrV3nIVfBR67r/tTU11KDLCqvIVfFSqtJdOnch+v//FypRNDzz+WNop38DguaHbNPiJWmp6c7A8Pd3U9+7K2vXvwH8X78Ny5dPreO6sRTVr+0tKfySfj69ZAYHZGx+F2W+XKjoqVXfdU0WDn6ip0LMWvTxyh159YacmTW+V77INGgfpp+nZr0j//O1xnQ+1aM32Hv92nQ91XaEvU9Y72yMuT59MUlBw+jgKJUt5af7KzhrUd60mf3ZITz2Xcy+NS1G6jJci8ni85tuv71aH28rq1TcbS5LGjP7b1WCrUctfjZsFa9qkwzp9Kkmdb89/PMHCvt+FWdbd3U3OnIf8KLSIcKscjn+Pt7LeuqNPRX32VfYnRFzqsZLXfs/rs+Z0SiOHbdPEaa10W9f076tubZfnWE5sbKpeGdso/daFXTF68pHN8i/hnuUWlct5PwqrMJ/dAfdW0ahndyg6yqp+91TJd/kD++L0+f/+0faDvRRc0lNxsWlF/jhEALjWcAsAAENwOtMHiRr0UDVN+b6NpnxxOMvz21ctOy+HQ0qx2PXt1KPq2iN7o6TvgCpatijUNRjUpvUR2rYpUj3vrJTjOnv3q6SpEw+7xgjYtD7C1b0+Mx8fsxwOKTnJnu92tG5XRtYUh3745rgkKSXFoXfH7FVEePYB5IaPrKvZv5zSnF9Py/FvAydjTAQPDze1aZ9+3/VP36aXdeF8in6cfkz97k4/cb7rnir6ZvJR1zPhJ316SCEVfdWoaZC696qolcvO658D6V2IF/6Z/ZYJSfLzd1fHLuX00bv7ZbM55XRK3049luPgZtYUuz54a59rf0VHpWrj2nBXl25vH7MSE9Mbkb5+Zt3eM0QTxh+Qw5EeTnz20UH1HVA5xyt2hdlvPj5mJSVmH1MhPz9OP6ZH710vq9WhkEo+atg4SHGxqfkud/pkkmZ8f1yt22d/6kBysk0pFrusVoeSk+ya+/tpOf69atumfRklJ9k0b076vj9xLFF3dFzhGmiuVGkvVa3up6++b61PPjig7VuK7nGWLVqXltVqd91is/fvWB059F938uRku6vBf+5MstasDMtytXnAvVX0xcf/6I4+FeXtnf+pSGHf78IsW6mKr04cT1R4WHrvij9nnSnUvlix5Lwiwq1yOqUpnx9S42ZBKlPWSz3vrKiFf57V3r9jJaU3kN95Y4+czryPlby+D/La73l91hwOpywWu+Li/vsuOn4sUQ5H9h4Azw/bpg/fSb+Xv2GTIIVU9MnWU+Jy3o/8tvFihfns3tq5nFKtDs39/bT6Dqic7/LJSTY5HU4lJdrkcKTf5iUpx54RAHCjoAcAgBvK3r9jVTFgVpbXxr3fVN4+Zh0/mqDpM9vKz99dQ5+po5FDt2nhX10kpXfZbV53gRIS0tTs5pIa8WL9bGU3bhakd/93kwb1XStPz/Ru4F9Ma6VqNXK+sv3kM3V1/pxFbZsukp+fuzy9zPri65bZ5qta3V8NGwfqpjrzNfWHNjmU9J8SAe76ZkZbPf/UNn06/oASE226654qWUbPz9CyTWlN+b6Nxr78t14euVOenm7y9jbrk0m3qGr19Dp/M6Otnn1yq/737n6lpNj12NDa6n9vegAw+MlaOn0qSa0bL5KXl1nlynvr65/ayN3dpOYtS+rp5+upZ8cVKhHgoZZtSkvKubvsJ5Na6Lknt6ppzXmSpPoNg9Srb8Vs8/1v4i0aM/pvtWq0UH5+7kpIsKnf3ZX17Kj096LdrWX13ti9alRtnrbu76UPP2+uEU9sVaNqf8rplNp2KKMx7za97P3WvVeIRjyxVfv2xGrm3A55vh+ZPTG8jvbsilGTGvNUooSHfP3c9dUPrXOcN/NxWrpMemPxlbGNs8334OAaWrowVA2rzJWPr7t69K6oyIj0e6N9/cz67KuWemH4Nr39+m5Zku167a0mqtcgQPv3xLrKaHJTsF57s7GGDd6sZeu75bjNheVfwl2fT22lZ5/cqjGj/1aVan6qmumq86jXG+r/HtioOb+dVqnSXmrQKFDnzvzXm6Tv3VU09uXduuvfq7QFUZj3uzDLtmxTWp26llebxovk7uGWa4+e3NStH6gBd6xWeFiKAgI99e0v7SSlj9/w1vhmuq/fWvn4mpWcZNPLYxvLZMr7WLn4+6Bjl//qk99+z+2zZjab9PaHzfT8U9v18nM7Va9BoGrXLZHlPcnw7sc3adijm9Wkxnw5HE41vTlYDz9Ws1D7ND9X6jvPbDap3z1VtGt7lKsXRF7LlylbSv3vrap2zRbLz989PZDyMevc2WRVr+lfoG0BgOuNyel0EnMCMLS2TRdrzLtN1bVHBSUl2gp0/2p0VKpKlipYQ8pmcyo2JlWlSnvlenXM4UgfdT2veS4WE50qb2+zfHzzH5wqMsIqs9mUa+MvNiZNJQLcXffeXlz/nPZLisUuk5tJtjSHoiKtatNksQ6d65dtQLcMyUl2paY6FBSc9/5NTXUoJjpVpct4ZatPSopDlmRblu1ITrLLzU0FHqSrIPstPi5N7u5u8vUr/MBfKRa7kpPtBT4+CiImOlUBgR45vj92u1NRkenHTk7TrySnU4qLTcvxPXU60+ud0344cypZvTuv1M7DvQtd58K+3wVdNi42TV7e5gL1SMjJxY+xzOB0pn/+goI9sw3EmNuxkt/3QV77Xcr9s5aa6pAl2V6g77i42DSZzaZcP8+Z13Up78eV+s4bOWybGjcN1pChtQq8fFKiTWaziYH+ABgCAQAAw8sIAHr0znlgOmQXHpainh1X6ukX6qlCiI8mf3Yo/d/vcr7iDWRYszJMkz87pJtblNKoN/If+A4oiLOnk7Vq+QV99M4+rd3Ro0h6ugDAjYgxAAAYXo/eFRVS0Sf/GeFStpy3pv7YRsePJujPWWfU567K+mxq9tsbgItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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'}, 'type': 'image_url'}], 'name': 'computer', 'role': 'tool', 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}], 'workflow_label': 'check_goal_status'}\n", + "--------------------------------------------------\n", + "Transition type: step\n", + "Transition output: {'cdp_url': 'wss://connect.browserbase.com/?apiKey=bb_live_fvKLOUF6VHrh78JFPRolwpPlQug&sessionId=4cfd1fca-7939-4d75-b563-81f1c19458f2', 'julep_session_id': '6d257b7c-d3f5-4720-b160-9ec7f012ef9a', 'messages': [{'content': [{'image_url': {'url': 'data:image/png;base64,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02rftq22bduebf6XXn5FdWvX1v89NuSy3yevQl49zTg+goMCUy0Wi2e3bt12zfljboOwsLDIF194/uzcuXNbSVJISEjY//738bG7+vdvbTKZrqkLBk6nU1FR0QoIKCFPT89s05OTk2W1prq6qV8uu90umUyK/ne0+t9mzdLades16YvPL6vcqKgolQgIcHWdL4xL3UZLSoqsKSkKCvpvOYvForS0NNcgmQURHRMjk0w5rt9msykuPj7Xkf1tNptiYmNVMjjY1QugKBT2swAARkQAAAA3mKIKADLY7XZFRUXJy9tbASVK5HjlMykpSUnJySpTunShptsdDrn927AqERCgY8eO6ZlnR2r+3Dmukccvll/jLzOHw6Go6Gj5+frK19c3x3liY2Pl4eHh6maduW5RUVEqGRwsd/eCDZmTkpKihMRElS5VKsf9cCUURQAwa/acel5eXj7h4eHhr77y8pEZM2a0cTgcbqVKlY5+7/339j/44ENtzGazYccNevm111W/bj21b99WF8IuaMKnX2jQvffo7v79i7tqyIQAAADyRwAAADeYog4ArqSfZ/6iY8eOa0D/u+R0OvXV1KkKCgrW22+OLe6qXTeKMgCQpNjY2Ji33hy3d+rUqW1tNpt7QEBA4pgxY3c88eSTrT09PfMeDfEGderUKf3w0886ePAflQgooW5du6h/v35yc7umOkYYHgEAAOSPAAAAbjDXUwCQkJCgH376WVu3bpfJZFLzW27Sow8/nG1EfeSuKAKA2XP+qO/p6emdMU9SUmLi+A/G7/jss09bW61WL19fX8tLo0Ztee65kS19fHxy7koBFDMCAADIHwEAANxgrqcAAJfvSgQAkpSSkmKZ+MUXWz744P0WiYmJfp6enqnDn35606uvvnZTiRIlCn6zOHCVEAAAQP7ouwYAALLx9vb2efa559q++977O4KCguJTU1M9P//ss/Yvvzx6d3R0Do9eAAAA1zwCAAAAkCMPDw/Pxx9/vN0nEybsKVOmbJTdbjdP/+ab9iOfe+7QhQsXwoq7fgAAoHAIAAAAQK7c3NzM9913f9tJkyYdqVy58nmn02n69ddf2gx/atjpU6dOnSnu+gEAgIIjAAAAAHkymUxufe68s/VXU6eG1q5d+5QkLVy4sMUTTzwefeiff44Wd/0AAEDBEAAAAIAC6dy5S/Np30yPb9Kk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gItNm3xE9RoG6rnR2W+zAS7V7l0xWrLgnL7+qS2NfwDIAz0AAAAAAAAwAHoAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAAYAAEAAAAAAAAGAABAAAAAAAABkAAAAAAAACAARAAAAAAAABgAAQAAAAAAAAYAAEAAAAAAAAGQAAAAAAAAIABEAAAAAAAAGAABAAAAAAAABgAAQAAAAAAAAZAAAAAAAAAgAEQAAAAAAAA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hgULFuihhx5SRESE9u/fr7vvvlvHjx+XJMXGxmrgwIFatGiRkpOTNWHCBD3//POSJKvVqkcffVQ//PCD0tLSNHv2bN1///2u7u3btm3Tvffeq927d+vQoUOu5TIcOXJE/fr1044dOxQTE6MnnnhC8+fPz3EbDh06pL59+2rTpk2KjY3VG2+8oTfffFOSlJCQ4Krv0aNHcxxbIa/tuNjMmTNd9Vi2bJlefvlljR8/XufPn9fMmTP10EMP6amnntK2bdt05swZDR8+XL///rtr+Y8++kivvvqqkpKS9Pfff+vOO+/U8ePHcz0W8tr/F8ut7Py28fDhw4qJiVFiYqIOHTokh8OhTZs26YsvvnCVPXfuXD300EM6d+6cTp8+rfvuu08LFiyQJNdxOHjw4ByP2WPHjundd9/Nsev+X3/9pWnTprl+f+ONNzR27FjFxcVp/fr16tu3r+uzlPnYl9I/W88884ymTZum2NhYffTRRxozZkyO+ya/z+msWbM0bNgwRUdH68CBAxowYIB27tyZY1kAgOsXPQAA4AbTqXYrfb72v3vYz4alaOXWKHW6paTc3P69Mu1I019R3+tun1fl4ZZzd+7IyEiVKlVKnp6e2aaVK1dOERERBa5T06ZNc7xloGXLlqpdu7bmzJmjIUOGqH79+q7xBQYNGqRHHnlEkuTh4aFVq1blek/24MGDdd9990mSNmzYoK1bt2rBggWqVKmSJOmxxx7Lcbm4uDhNmzZN48aNU+/evSVJkydP1qxZsySlX3F9++239c4776hbt26SpNGjR2v69Ol655139OOPP8rPz0/Tpk2Tu7u7hgwZom7dumn37t3y8/NTxYoV9dZbb8nLy0vJyclq3769/v77b7Vu3VqfffaZ7rzzTldDffv27Ro8eLCrbu+995569OihV199VZJUt25dffnll+rTp0+27fjwww/VtWtXvfXWW659179/f91555266aab9OSTT2rRokV69tlnXVeTM8trO3J6/zMrWbKkpk2bJjc3Nz344IPq2bOn7rzzTj3xxBOSpICAAC1dulT33HOPbDabazDCunXrSpIeeughLVy4UM8880y2YyG//Z9ZfmXntY0vvPCCrFarLly44NrfmcXHx+ujjz7S2LFjXfu/WbNmev/993Xbbbe5rrj3798/x2O2UaNG2rRpU449KjLbvn27FixY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AcDw4cM1Z84cV7lvv/223nzzTSUlJWn+/Pl68MEHXftv1qxZGjZsmKKjo3XgwAENGDBAO3fuzLcO14M33nhDY8eOVVxcnNavX6++ffvq2LFjkqTFixfr999/d837/vvvZzluZs2alePtMdWrV5fD4dCvv/6a5V7FWbNmuRr/CxYs0EMPPaSIiAjt379fd999t44fPy4p/Xi+//77debMGYWHh2vw4MGuz0XGezlkyBAtWbJE58+fV2xsrAYOHKhFixYpOTlZEyZM0PPPP5+lTuPGjcv1vc9w8ODBLNv3yy+/aNSoUUpMTJQk7dixQ59++ql8fX3z3XcrVqzQq6++qtGjR2vXrl3ZemPMmjVLo0aNUtmyZSVd/8cRcKMKCAhQWlqaHA5Hjp/rzH+3ly5dqh9//NG17MaNG/XJJ5/Iz88vz+81Sfr+++/11FNPKTo6Wvv27dOAAQO0e/duxcXF6aWXXlJkZKSk9BD0lVde0enTp6/ujiiE5ORkPfLII5o5c6aSkpI0YcIEjRs3TlLO3+GHDx/W2LFjNWLECJ04cUKbNm3Svffeq9GjR2vGjBmKjY3VRx99pNdee821jpdfflmffPKJK5gdMGCAYmNji2eD/1WzZk2tWLFCYWFhrtd69erlep/z2i+HDx9WTEyMEhMTdejQIUVFReno0aOSpJMnTyo8PFxS1vPE/M79tmzZonvvvVd79uzRgQMHNHjwYI0bN851LOV1bgNcafQAwFWzf/9+nThxQj169FBAQIB8fHy0cuVK1xXMDRs2aOvWrVqwYIErzX7sscckpXdp6969u5YtW6Zu3bpJSh8gcMSIEZKk9957Tz169NCrr74qSapbt66+/PJL9enTR1L6H+1PP/1UderUkSTdeuut2rp1q7p16+a6BSCznTt36ty5c5ozZ47c3d01cOBAjRo1SpGRka6rjJk5nU598803CgwMVHh4uLp06aKjR48qJCRE06dP17hx41zd6r/44gvNnj27yPbr5Zg7d662b98um82mgwcPqlWrVjnuj5wsXrxYjRs31nvvvSdJuuWWW7Rw4cJc5+/fv78eeeQRSZKHh4dWrVql/v37a+vWrfrzzz+1YMEClS9fXjabTb1799bixYvVt29fzZkzRw899JCGDh0qKb2b37Zt23TzzTcXug5X0+eff+5K+TMcPXpUdevWlSRt375dCxYs0Lx581xXrEaPHq0PPvhAX3/9tTp06KBZs2ZJSu/FcvbsWaWmprqOwW3btmngwIHZ1luzZk2NGTNGH330kX788Uf16tVLvXv3dl1ZSEpK0ttvv6133nnH9VkaPXq0pk+frnfeeUeHDx/WG2+84fpclihRQr///rvrsyRJgwcP1n333Scp/Xj28/PTtGnT5O7uriFDhqhbt27avXu360rMoEGDcnzvM+vQoYM+/vhjxcXFKTAwUKtXr1bJkiW1YcMGde/eXVu2bFG7du3k5uaW776TJG9vb82ePVt+fn5Z1rNw4UJ98MEHmjhxopo0aSKp8McygCvL6XTq1KlTmjZtmlq3bu3qfZfb51qSevfurY8++kivvPKKTCaTli9frm7dusnLyyvP77X4+HhNmjRJb731lrp37y5J+t///qdJkyZp0qRJKlmypJYvX6777rtPf//9t5KSklw9h65F3333nWw2m37++WeZzWYNHDhQd955px5//HFX6Jn5O3zNmjWy2Wz64osvVKpUKTkcDvXs2VMxMTGaOnWqJKl169Z64YUX9N577yktLU2LFi3Sd999p+bNm8tut+uZZ57R3r171aFDh2Lb7tGjR+vFF19Uz5491aFDB/Xu3Vu33nqr65aIvPbLCy+8IKvVqgsXLrjOIxs2bKg5c+ZoyJAhrlsALpbbuV+DBg30+eefq2/fvnrzzTclSZs2bdITTzzhWjavcxvgSiMAwFUzd+5cdejQQUFBQZLSk9m5c+e6/iDv3r1bTZs2zdKVLaNBKEl9+vTR0KFDlZKSomPHjik2NladO3eW1WrV9u3b1bBhQ82dO1dS+lgD586dU0xMjCTJzc3N1fgviDp16sjb21uvv/66evXqpRYtWmjGjBm5zl+1alUFBgZKksqWLSs3NzdFR0crMTFRqampWboPVq9evcD1uNIaNmyojh07yul06uabb9ZPP/2kNWvWFOjk5uabb9bPP/+szz77TJ06dVKXLl1cDcqcNG7c2PX/ChUq6MiRI5LSr9KULVtWmzdvdk0vWbKk9u/fr759+6p58+aaNWuW/P391aFDB1focyl1uJoaNmyoihUrZnnt8OHDrv9v2rRJzZs3z9L1vn///ho6dKjS0tLUoUMHvfvuu4qJidHq1avVsWNHJSUl6a+//tLtt9+ugwcPqn379jmu++6771aXLl20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'}, 'type': 'image_url'}], 'name': 'computer', 'role': 'tool', 'tool_call_id': 'toolu_01SF7tWSGgctWmwTWqET1zbV'}], 'workflow_label': 'run_browser'}\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in reverse chronological order\n", + "for transition in reversed(transitions):\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/07-Integrating_External_Tools_and_APIs.py b/cookbooks/07-Integrating_External_Tools_and_APIs.py deleted file mode 100644 index fa93f687a..000000000 --- a/cookbooks/07-Integrating_External_Tools_and_APIs.py +++ /dev/null @@ -1,126 +0,0 @@ -import uuid -import yaml -from julep import Client - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an "agent" -name = "Multi-Tool Analyst" -about = "An AI agent capable of using multiple external tools and APIs to gather and analyze information." - -# Create the agent -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name=name, - about=about, - model="gpt-4-turbo", -) - -# Defining a Task -task_def = yaml.safe_load(""" -name: Comprehensive Analysis Report - -input_schema: - type: object - properties: - topic: - type: string - description: The main topic to analyze. - location: - type: string - description: A location related to the topic for weather and news analysis. - -tools: -- name: brave_search - type: integration - integration: - provider: brave - setup: - api_key: "YOUR_BRAVE_API_KEY" - -- name: weather - type: integration - integration: - provider: weather - setup: - openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY" - -- name: wikipedia - type: integration - integration: - provider: wikipedia - -main: -- tool: brave_search - arguments: - query: "{{inputs[0].topic}} latest developments" - -- tool: weather - arguments: - location: "{{inputs[0].location}}" - -- tool: wikipedia - arguments: - query: "{{inputs[0].topic}}" - -- prompt: - - role: system - content: >- - You are a comprehensive analyst. Your task is to create a detailed report on the topic "{{inputs[0].topic}}" - using the information gathered from various sources. Include the following sections in your report: - - 1. Overview (based on Wikipedia data) - 2. Latest Developments (based on Brave Search results) - 3. Weather Impact (if applicable, based on weather data for {{inputs[0].location}}) - 4. Analysis and Conclusions - - Use the following data for your report: - - Brave Search Results: {{outputs[0]}} - Weather Data: {{outputs[1]}} - Wikipedia Data: {{outputs[2]}} - - Provide a well-structured, informative report that synthesizes information from all these sources. - unwrap: true - -- return: _ -""") - -# Creating/Updating a task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Creating an Execution -execution = client.executions.create( - task_id=task.id, - input={ - "topic": "Renewable Energy", - "location": "Berlin, Germany" - } -) - -print(f"Execution ID: {execution.id}") - -# Getting the execution details -execution = client.executions.get(execution.id) -print("Execution Output:") -print(execution.output) - -# List all steps of the executed task -print("Execution Steps:") -for item in client.executions.transitions.list(execution_id=execution.id).items: - print(item) - -# Stream the execution steps in real-time -print("Streaming Execution Steps:") -for step in client.executions.transitions.stream(execution_id=execution.id): - print(step) \ No newline at end of file diff --git a/cookbooks/07-personalized-research-assistant.ipynb b/cookbooks/07-personalized-research-assistant.ipynb new file mode 100644 index 000000000..9e887238d --- /dev/null +++ b/cookbooks/07-personalized-research-assistant.ipynb @@ -0,0 +1,4141 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"julep\"\n", + "
\n", + "\n", + "

\n", + "
\n", + " Explore Docs (wip)\n", + " ·\n", + " Discord\n", + " ·\n", + " 𝕏\n", + " ·\n", + " LinkedIn\n", + "

\n", + "\n", + "

\n", + " \"NPM\n", + "  \n", + " \"PyPI\n", + "  \n", + " \"Docker\n", + "  \n", + " \"GitHub\n", + "

\n", + "\n", + "## Task Definition: Develop a Personalized Research Paper Recommendation Assistant\n", + "\n", + "### Overview\n", + "\n", + "This task is a personal research paper assistant that can search the arxiv for information and recommend research papers to the user based on their persona. It further uses the llama parse tool to scrape the research papers and extract the contents.\n", + "\n", + "### Task Tools:\n", + "\n", + "**brave_search**: An `integration` type tool that can search the web and extract data from a given URL.\n", + "\n", + "**arxiv_search**: An `integration` type tool that can search the arxiv for research papers.\n", + "\n", + "**llama_parse_scrape**: An `integration` type tool that can scrape the web and extract data from a given URL.\n", + "\n", + "**get_user_from_ppid**: A `system` type tool that can get a user from the user ppid.\n", + "\n", + "**list_user_docs**: A `system` type tool that can list the user docs.\n", + "\n", + "**get_user_docs**: A `system` type tool that can get the user docs.\n", + "\n", + "**create_agent_doc**: A `system` type tool that can create an agent doc.\n", + "\n", + "**create_user_doc**: A `system` type tool that can create a user doc.\n", + "\n", + "### Task Input:\n", + "\n", + "**topics**: A list of topics to search for.\n", + "\n", + "**user_ppid**: The user ppid to search for.\n", + "\n", + "### Task Output:\n", + "\n", + "**output**: A list of research papers that are recommended to the user.\n", + "\n", + "### Task Flow\n", + "\n", + "1. **Brave Search**: The `brave_search` tool is called to search the web for the topics.\n", + "\n", + "2. **Prompt Step**: The prompt step is used to extract the keywords from the search results.\n", + "\n", + "3. **Evaluate**: The keywords are evaluated to get the top 8 keywords.\n", + "\n", + "4. **Arxiv Search**: The `arxiv_search` tool is called to search the arxiv for the keywords.\n", + "\n", + "5. **Evaluate**: The arxiv results are evaluated to get the top 2 research papers for each keyword.\n", + "\n", + "6. **Get User**: The `get_user` tool is called to get the user from the user ppid.\n", + "\n", + "7. **Get User Docs**: The `get_user_docs` tool is called to get the user docs. In this case, we are getting the user persona.\n", + "\n", + "8. **Prompt Step**: The prompt step is used to select the top 5 research papers based on the user persona and the research papers.\n", + "\n", + "9. **Evaluate**: The research papers are evaluated to get the top 5 research papers.\n", + "\n", + "10. **Llama Parse Scrape**: The `llama_parse_scrape` tool is called to scrape the research papers.\n", + "\n", + "11. **Evaluate**: The scraped research papers are evaluated to get the paper contents.\n", + "\n", + "12. **Create User Doc**: The `create_user_doc` tool is called to create a user doc with the paper contents.\n", + "\n", + "```plaintext\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + "| Brave Search | | Extract | | Evaluate | | Arxiv Search |\n", + "| (for topics) | --> | Keywords | --> | Top 8 | --> | (for keywords)|\n", + "| | | (Prompt Step) | | Keywords | | |\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + " |\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + "| Select Top 5 | | Get User | | Get User | | Evaluate |\n", + "| Papers | <-- | from PPID | <-- | Docs (Persona)| <-- | Arxiv Results |\n", + "| (Prompt Step) | | | | | | |\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + " |\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + "| Evaluate | | Llama Parse | | Evaluate | | Create User |\n", + "| Paper Titles | --> | Scrape | --> | Paper Contents| --> | Doc |\n", + "| and URLs | | (for URLs) | | | | |\n", + "+----------------+ +----------------+ +----------------+ +----------------+\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation\n", + "\n", + "To recreate the notebook and see the code implementation for this task, you can access the Google Colab notebook using the link below:\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "### Additional Information\n", + "\n", + "For more details about the task or if you have any questions, please don't hesitate to contact the author:\n", + "\n", + "**Author:** Julep AI \n", + "**Contact:** [hey@julep.ai](mailto:hey@julep.ai) or Discord" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Global UUID is generated for agent and task\n", + "from dotenv import load_dotenv\n", + "from julep import Client\n", + "import os\n", + "import uuid\n", + "\n", + "load_dotenv()\n", + "\n", + "# NOTE: these UUIDs are used in order not to use the `create_or_update` methods instead of\n", + "# the `create` methods for the sake of not creating new resources every time a cell is run.\n", + "AGENT_UUID = uuid.uuid4()\n", + "TASK_UUID = uuid.uuid4()\n", + "SESSION_UUID = uuid.uuid4()\n", + "USER_UUID = uuid.uuid4()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Julep Client with the API Key" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from julep import Client\n", + "import os\n", + "\n", + "api_key = os.getenv(\"JULEP_API_KEY\")\n", + "\n", + "# Create a Julep client\n", + "client = Client(api_key=api_key, environment=\"dev\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an \"agent\"\n", + "\n", + "Agent is the object to which LLM settings, like model, temperature along with tools are scoped to.\n", + "\n", + "To learn more about the agent, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#agent)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Defining the agent\n", + "name = \"Research Assistant\"\n", + "about = \"The Research Assistant is a research assistant that can search the arxiv for information.\"\n", + "default_settings = {\n", + " \"temperature\": 0.7,\n", + " \"top_p\": 1,\n", + " \"min_p\": 0.01,\n", + " \"presence_penalty\": 0,\n", + " \"frequency_penalty\": 0,\n", + " \"length_penalty\": 1.0,\n", + " \"max_tokens\": 150,\n", + "}\n", + "\n", + "# Create the agent\n", + "agent = client.agents.create_or_update(\n", + " agent_id=AGENT_UUID,\n", + " name=name,\n", + " about=about,\n", + " model=\"gpt-4o\",\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a \"user\"\n", + "\n", + "User is the object to which persona, metadata, and documents are scoped to.\n", + "\n", + "To learn more about the user, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#user)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ResourceCreated(id='70ba3a2c-b5ab-4074-8f01-fe9f81771c17', created_at=datetime.datetime(2024, 11, 28, 20, 14, 56, 748977, tzinfo=datetime.timezone.utc), jobs=[])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a user with metadata\n", + "client.users.create_or_update(\n", + " user_id=USER_UUID,\n", + " name = f\"user_new_1\",\n", + " about = \"This is a test user\",\n", + " metadata={\n", + " \"ppid\": str(USER_UUID), # Ensure it's a native int\n", + " \"age\": int(25), # Ensure it's a native int\n", + " \"state\": str(\"California\"), # Convert to string if not already\n", + " \"city\": str(\"San Francisco\"), # Convert to string if not already\n", + " \"research_interests\": str([\"Quantum Computing\", \"Artificial Intelligence\", \"Machine Learning\"]), # Convert to string or json serializable format\n", + " \"favorite_scientists\": str([\"Richard Feynman\", \"Alan Turing\", \"Geoffrey Hinton\"]), # Same as above\n", + " \"top_research_area\": str(\"Quantum Computing\"),\n", + " \"top_scientist\": str(\"Richard Feynman\"),\n", + " \"latest_research_read\": str(\"Quantum Machine Learning\"),\n", + " \"top_journals\": str([\"Nature\", \"Science\", \"IEEE Transactions on Quantum Engineering\"])\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a \"user\" document\n", + "\n", + "User documents are used to store the persona of the user." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ResourceCreated(id='ec3af6b0-b2a6-4d84-8987-ddffafc0516e', created_at=datetime.datetime(2024, 11, 28, 20, 14, 58, 426298, tzinfo=datetime.timezone.utc), jobs=['139125b5-3c35-4803-99d4-48f829a7fb5a'])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a user document\n", + "client.users.docs.create(\n", + " user_id=USER_UUID,\n", + " title=\"Google Scholar Persona\",\n", + " embed_instruction=\"Represent the query for User persona used for document retrieval: \",\n", + " content=\"This is a test google scholar persona. The user is a 25 year old living in San Francisco. He is a fan of Google Scholar and is a student at Stanford University.\\\n", + " He uses Google Scholar to stay updated with the latest research in quantum computing and artificial intelligence.\\\n", + " Over the last 3 months, he has read 100+ research papers on quantum computing and artificial intelligence.\\\n", + " He is particularly interested in the latest research in quantum machine learning and quantum error correction.\\\n", + " He is also interested in how LLMs are being used to solve problems in quantum mechanics.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a Task\n", + "\n", + "Tasks in Julep are Github-Actions-style workflows that define long-running, multi-step actions.\n", + "\n", + "You can use them to conduct complex actions by defining them step-by-step.\n", + "\n", + "To learn more about tasks, please refer to the `Tasks` section in [Julep Concepts](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#tasks)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "task_def = yaml.safe_load(\"\"\"\n", + "name: Personalized Research Paper Recommendation Assistant\n", + "\n", + "input_schema:\n", + " type: object\n", + " properties:\n", + " topics:\n", + " type: array\n", + " items:\n", + " type: string\n", + " description: The topics to search for.\n", + " user_id:\n", + " type: string\n", + " description: The user id to search for.\n", + "\n", + "tools:\n", + " \n", + "- name: brave_search\n", + " type: integration\n", + " integration:\n", + " provider: brave\n", + " setup:\n", + " api_key: \"DEMO_API_KEY\"\n", + " \n", + "- name: arxiv_search\n", + " type: integration\n", + " integration:\n", + " provider: arxiv\n", + " \n", + "- name: llama_parse_scrape\n", + " type: integration\n", + " integration:\n", + " provider: llama_parse\n", + " setup:\n", + " llamaparse_api_key: \"DEMO_API_KEY\"\n", + " params:\n", + " result_format: \"text\"\n", + " num_workers: 3\n", + "\n", + "- name: get_user_from_id\n", + " description: Get a user from the user id\n", + " type: system\n", + " system:\n", + " resource: user\n", + " operation: list\n", + "\n", + "- name: list_user_docs\n", + " description: List the user docs\n", + " type: system\n", + " system:\n", + " resource: user\n", + " subresource: doc\n", + " operation: list\n", + "\n", + "- name: get_user_docs\n", + " description: Get user docs\n", + " type: system\n", + " system:\n", + " resource: user\n", + " subresource: doc\n", + " operation: list\n", + " \n", + "- name : create_agent_doc\n", + " description: Create an agent doc\n", + " type: system\n", + " system:\n", + " resource: agent\n", + " subresource: doc\n", + " operation: create\n", + " \n", + "- name: create_user_doc\n", + " description: Create a user doc\n", + " type: system\n", + " system:\n", + " resource: user\n", + " subresource: doc\n", + " operation: create\n", + "\n", + "main:\n", + "\n", + "# Calling brave search for each topic\n", + "- over: _.topics\n", + " parallelism: 2\n", + " map:\n", + " tool: brave_search\n", + " arguments:\n", + " query: \"'the latest news about ' + _\"\n", + "\n", + "# Prompting the agent to extract keywords from the news snippet\n", + "- prompt:\n", + " - role: system\n", + " content: |\n", + " You are a research assistant.\n", + " Based on the following latest html news snippet, generate keywords:\n", + " \n", + " {% for docs in _ %}\n", + " {% for doc in docs['result'] %}\n", + " {{doc['snippet']}}\n", + " {% endfor %}\n", + " {% endfor %}\n", + " \n", + " Your response should be a list of keywords, separated by commas. Do not add any other text.\n", + " Example: `KEYWORDS: keyword1, keyword2, keyword3`\n", + " unwrap: true\n", + "\n", + "# Extracting the top 8 keywords\n", + "- evaluate:\n", + " keywords: str(_).split(',')[:8]\n", + "\n", + "# Calling arxiv search for each keyword\n", + "- over: _.keywords\n", + " parallelism: 4\n", + " map:\n", + " tool: arxiv_search\n", + " arguments:\n", + " query: _\n", + " max_results: \"2\"\n", + "\n", + "# Get the arxiv results\n", + "- evaluate:\n", + " arxiv_results: _\n", + " \n", + "# Get the user from the id using metadata_filter (returns a list)\n", + "- tool: get_user_from_id\n", + " arguments:\n", + " limit: \"1\"\n", + " sort_by: \"'created_at'\"\n", + " direction: \"'desc'\"\n", + " metadata_filter:\n", + " ppid: inputs[0]['user_id']\n", + "\n", + "# Unwrap the list to get the user\n", + "- evaluate:\n", + " user_info: _[0]\n", + "\n", + "# Get the user persona from user docs\n", + "- tool: get_user_docs\n", + " arguments:\n", + " user_id: inputs[0]['user_id']\n", + " limit: \"1\"\n", + " sort_by: \"'created_at'\"\n", + " direction: \"'desc'\"\n", + "\n", + "# Unwrap the list to get the user persona\n", + "- evaluate:\n", + " persona: _[0].content[0]\n", + "\n", + "# Prompting the agent to select the top 5 papers\n", + "- prompt:\n", + " - role: system\n", + " content: |\n", + " As an expert in recommending research papers tailored to individual user personas, your task is to select papers top 5 papers that align closely with the user's interests and professional needs. \n", + " This careful selection ensures that the recommendations are both relevant and beneficial to the user's ongoing research and professional development.\n", + "\n", + " Below is a detailed description of the user's persona:\n", + " {{_.persona}}\n", + "\n", + " Based on this persona, please review the following list of research paper titles. \n", + " These titles have been gathered from a comprehensive search to match the user's interests:\n", + "\n", + " {% for result in outputs[4].arxiv_results %}\n", + " {% for doc in result['result'] %}\n", + " - (Title: {{doc['title']}}, \n", + " \n", + " - URL: {{doc['pdf_url']}})\n", + " {% endfor %}\n", + " {% endfor %}\n", + "\n", + " After reviewing the titles, please compile a list of the top 5 relevant research papers. \n", + " Your response should be a list of Titles and URLs of the papers, separated by double commas. \n", + " Do not add any other text.\n", + " Example: `Title1,url1,,Title2,url2,,Title3,url3`\n", + " unwrap: true\n", + "\n", + "# Extracting the titles and urls\n", + "- evaluate:\n", + " paper_titles_and_urls: |-\n", + " list(\n", + " zip(\n", + " [pair.split(',')[0] for pair in str(_).split(',,')],\n", + " [pair.split(',')[1].replace(\"http://\", \"https://\") for pair in str(_).split(',,')]\n", + " )\n", + " )\n", + " \n", + "# Calling llama parse scrape for each url\n", + "- over: _['paper_titles_and_urls']\n", + " parallelism: 5\n", + " map:\n", + " tool: llama_parse_scrape\n", + " arguments:\n", + " file: _[1]\n", + " filename: _[0]\n", + " params:\n", + " result_format: \"'text'\"\n", + "\n", + "# Extracting the paper contents\n", + "- evaluate:\n", + " paper_contents: str(_)\n", + "\n", + "# Create a new persona document\n", + "- tool: create_user_doc\n", + " arguments:\n", + " user_id: inputs[0]['user_id']\n", + " data:\n", + " embed_instruction: \"'Represent the query for User persona used for document retrieval: '\"\n", + " title: \"'Research Papers'\"\n", + " content: _['paper_contents']\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notes:\n", + "- The `unwrap:True` in the prompt step is used to unwrap the output of the prompt step (to unwrap the `choices[0].message.content` from the output of the model).\n", + "- The `metadata_filter` is used to filter the user based on the ppid.\n", + "- The `persona` is the user persona that is used to filter the research papers.\n", + "- The `paper_titles_and_urls` is the list of paper titles and urls that are used to call the `llama_parse_scrape` tool.\n", + "- The `{{}}` is used to access the output of the previous step. Whereas `{}` is used for jinja templating." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating/Updating a task" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Create the task\n", + "task = chat_task = client.tasks.create_or_update(\n", + " task_id=TASK_UUID,\n", + " agent_id=AGENT_UUID,\n", + " **task_def\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Execution\n", + "\n", + "Executions are the objects that run the task.\n", + "\n", + "To learn more about executions, please refer to the [documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md#execution)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "execution = client.executions.create(\n", + " task_id=TASK_UUID,\n", + " input={\n", + " \"topics\": [\"Quantum Computing\", \"LLMs\"],\n", + " \"user_id\": str(USER_UUID)\n", + " }\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are multiple ways to get the execution details and the output:\n", + "\n", + "1. **Get Execution Details**: This method retrieves the details of the execution, including the output of the last transition that took place.\n", + "\n", + "2. **List Transitions**: This method lists all the task steps that have been executed up to this point in time, so the output of a successful execution will be the output of the last transition (first in the transition list as it is in reverse chronological order), which should have a type of `finish`.\n", + "\n", + "\n", + "Note: You need to wait for a few seconds for the execution to complete before you can get the final output, so feel free to run the following cells multiple times until you get the final output.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transition number: 0\n", + "Transition type: finish\n", + "Transition output: {'created_at': '2024-11-28T20:15:42.025043Z', 'id': 'c9932fdf-6ec7-449b-af0d-36ec80a22f73', 'jobs': ['0ffe21ef-a536-43b2-a948-9766146dd6e5']}\n", + "--------------------------------------------------\n", + "Transition number: 1\n", + "Transition type: step\n", + "Transition output: {'paper_contents': '[{\\'documents\\': []}, {\\'documents\\': [{\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'5dde80d8-9fea-4e79-a8c6-4f56f6989bc5\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'arXiv:quant-ph/0003151v1 31 Mar 2000\\\\n\\\\n Unconventional Quantum Computing Devices\\\\n\\\\n Seth Lloyd\\\\n Mechanical Engineering\\\\n MIT 3-160\\\\n Cambridge, Mass. 02139\\\\n\\\\n Abstract: This paper investigates a variety of unconventional quantum computation de-\\\\n vices, including fermionic quantum computers and computers that exploit nonlinear quan-\\\\n tum mechanics. It is shown that unconventional quantum computing devices can in prin-\\\\n ciple compute some quantities more rapidly than ‘conventional’ quantum computers.\\\\n\\\\n Computers are physical: what they can and cannot do is determined by the laws\\\\n of physics. When scientific progress augments or revises those laws, our picture of what\\\\n computers can do changes. Currently, quantum mechanics is generally accepted as the\\\\n fundamental dynamical theory of how physical systems behave. Quantum computers can\\\\n in principle exploit quantum coherence to perform computational tasks that classical com-\\\\n puters cannot [1-21]. If someday quantum mechanics should turn out to be incomplete\\\\n or faulty, then our picture of what computers can do will change. In addition, the set\\\\n of known quantum phenomena is constantly increasing: essentially any coherent quantum\\\\n phenomenon involving nonlinear interactions between quantum degrees of freedom can\\\\n in principle be exploited to perform quantum logic. This paper discusses how the revi-\\\\n sion of fundamental laws and the discovery of new quantum phenomena can lead to new\\\\n technologies and algorithms for quantum computers.\\\\n Since new quantum effects are discovered seemingly every day, let’s first discuss two\\\\n basic tests that a phenomenon must pass to be able to function as a basis for quantum\\\\n computation. These are 1) The phenomenon must be nonlinear, and 2) It must be coherent.\\\\n To support quantum logic, the phenomenon must involve some form of nonlinearity, e.g.,\\\\n a nonlinear interaction between quantum degrees of freedom. Without such a nonlinearity\\\\n quantum devices, like linear classical devices, cannot perform even so simple a nonlinear\\\\n operation as an AND gate. Quantum coherence is a prerequisite for performing tasks\\\\n such as factoring using Shor’s algorithm [10], quantum simulation a la Feynman [11] and\\\\n Lloyd [12], or Grover’s data-base search algorithm [13], all of which require extended\\\\n manipulations of coherent quantum superpositions.\\\\n\\\\n 1\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'066b2185-7165-468b-84f7-b8866f9baa4f\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' The requirements of nonlinearity and coherence are not only necessary for a phe-\\\\nnomenon to support quantum computation, they are also in principle sufficient. As shown\\\\nin [14-15], essentially any nonlinear interaction between quantum degrees of freedom suf-\\\\nfices to construct universal quantum logic gates that can be assembled into a quantum\\\\ncomputer. In addition, the work of Preskill et al. [18] on robust quantum computation\\\\nshows that an error rate of no more than 10−4 per quantum logic operation allows one to\\\\nperform arbitrarily long quantum computations in principle.\\\\n In practice, of course, few if any quantum phenomena are likely to prove sufficiently\\\\ncontrollable to provide extended quantum computation. Promising devices under current\\\\nexperimental investigation include ion traps [5,7], high finesse cavities for manipulating\\\\nlight and atoms using quantum electrodynamics [6], and molecular systems that can be\\\\nmade to compute using nuclear magnetic resonance [8-9]. Such devices store quantum\\\\ninformation on the states of quantum systems such as photons, atoms, or nuclei, and\\\\naccomplish quantum logic by manipulating the interactions between the systems via the\\\\napplication of semiclassical potentials such as microwave or laser fields. We will call such\\\\ndevices ‘conventional’ quantum computers, if only because such devices have actually been\\\\nconstructed.\\\\n There is another sense in which such computers are conventional: although the de-\\\\nvices described above have already been used to explore new regimes in physics and to\\\\ncreate and investigate the properties of new and exotic quantum states of matter, they\\\\nfunction according to well established and well understood laws of physics. Perhaps the\\\\nmost striking examples of the ‘conventionality’ of current quantum logic devices are NMR\\\\nquantum microprocessors that are operated using techniques that have been refined for\\\\nalmost half a century. Ion-trap and quantum electrodynamic quantum computers, though\\\\ncertainly cutting edge devices, operate in a quantum electrodynamic regime where the\\\\nfundamental physics has been understood for decades (that is not to say that new and\\\\nunexpected physics does not arise frequently in this regime, rather that there is general\\\\nagreement on how to model the dynamics of such devices).\\\\n Make no mistake about it: a conventional quantum logic device is the best kind of\\\\nquantum logic device to have around. It is exactly because the physics of nuclear magnetic\\\\nresonance and quantum electrodynamics are well understood that devices based on this\\\\nphysics can be used systematically to construct and manipulate the exotic quantum states\\\\nthat form the basis for quantum computation. With that recognition, let us turn to\\\\n\\\\n 2\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'bcf5f805-d98f-4076-aa8c-f753cf7dbfa1\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'‘unconventional’ quantum computers.\\\\n Perhaps the most obvious basis for an unconventional quantum computer is the use\\\\nof particles with non-Boltzmann statistics in a refime where these statistics play a key role\\\\nin the dynamics of the device. For example, Lloyd [16] has proposed the use of fermions\\\\nas the fundamental carriers of quantum information, so that a site or state occupied by a\\\\nfermion represents a 1 and an unoccupied site or state represents a 0. It is straightforward\\\\nto design a universal quantum computer using a conditional hopping dynamics on an array\\\\nof sites, in which a fermion hops from one site to another if only if other sites are occupied.\\\\n If the array is one-dimensional, then such a fermionic quantum computer is equivalent\\\\nto a conventional quantum computer via the well-known technique of bosonization. If the\\\\narray is two or more dimensional, however, a local operation involving fermions on the\\\\nlattice cannot be mocked up by a local operation on a conventional quantum computer,\\\\nwhich must explicitly keep track of the phases induced by Fermi statistics. As a result,\\\\nsuch a fermionic computer can perform certain operations more rapidly than a conventional\\\\nquantum computer. An obvious example of a problem that can be solved more rapidly on\\\\na fermionic quantum computer is the problem of simulating a lattice fermionic system in\\\\ntwo or more dimensions. To get the antisymmetrization right in second quantized form,\\\\na conventional ‘Boltzmann’ quantum computer takes time proportional to T ℓd−1 where T\\\\nis the time over which the simulation is to take place, ℓ is the length of the lattice and\\\\nd is the dimension, while a fermionic quantum computer takes time proportional to T .\\\\n(Here we assume that the computations for both conventional and Fermionic quantum\\\\ncomputers can take advantage of the intrinsic parallelizability of such simulations: if the\\\\ncomputations are performed serially an additional factro of ℓd is required for both types\\\\nof computer to update each site sequentially.)\\\\n As the lattice size ℓ and the dimension d grow large, the difference between the two\\\\ntypes of computer also grows large. Indeed, the problem of simulating fermions hopping\\\\non a hypercube of dimension d as d → ∞ is evidently exponentially harder on a con-\\\\nventional quantum computer than a Fermionic quantum computer. Since a variety of\\\\ndifficult problems such as the travelling-salesman problem and data-base search problem\\\\ncan be mapped to particles hopping on a hypercube, it is interesting to speculate whether\\\\nfermionic computers might provide an exponential speed-up on problems of interest in ad-\\\\ndition to quantum simulation. No such problems are currently known, however. Fermionic\\\\ncomputers could be realized in principle by manipulating the ways in which electrons and\\\\n\\\\n 3\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'53057fa4-ba5b-423b-a686-13c6953d5f12\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'holes hop from site to site on a semiconductor lattice (though problems of decoherence are\\\\nlikely to be relatively severe for such systems).\\\\n It might also be possible to construct bosonic computers using photons, phonons, or\\\\natoms in a Bose-Einstein condensate. Such systems can be highly coherent and support\\\\nnonlinear interactions: phonons and photons can interact in a nonlinear fshion via their\\\\ncommon nonlinear interaction with matter, and atoms in a Bose condensate can be made\\\\nto interact bia quantum electrodynamics (by introduction of a cavity) or by collisions. So\\\\nfar, however, the feature of Bose condensates that makes them so interesting from the point\\\\nof view of physics — all particles in the same state — makes them less interesting from the\\\\npoint of view of quantum computation. Many particles in the same state, which can be\\\\nmanipulated coherently by a variety of techniques, explore the same volume of Hilbert space\\\\nas a single particle in that state. As a result, it is unclear how such a bosonic system could\\\\nprovide a speed-up over conventional quantum computation. More promising than Bose\\\\ncondensates from the perspective of quantum computation and quantum communications,\\\\nis the use of cavity quantum electrodynamics to ‘dial up’ or synthesize arbitrary states\\\\nof the cavity field. Such a use of bosonic states is important for the field of quantum\\\\ncommunications, which requires the ability to create and manipulate entangled states of\\\\nthe electromagnetic field.\\\\n A third unconventional design for a quantum computer relies on ‘exotic’ statistics\\\\nthat are neither fermionic nor bosonic. Kitaev has recently proposed a quantum computer\\\\narchitecture based on ‘anyons,’ particles that when exchanged acquuire an arbitrary phase.\\\\nExamples of anyons include two-dimensional topological defects in lattice systems of spins\\\\nwith various symmetries. Kitaev noted that such anyons could perform quantum logic via\\\\nAharonov-Bohm type interactions [19]. Preskill et al. have shown explicitly how anyonic\\\\nsystems could compute in principle [20], and Lloyd et al. have proposed methods of\\\\nrealizing anyons using superconducting circuits (they could also in principle be constructed\\\\nusing NMR quantum computers to mock up the anyonic dynamics in an effectively two-\\\\ndimensional space of spins) [21]. The advantage of using anyons for quantum computation\\\\nis that their nonlocal topological nature can make them intrinsically error-correcting and\\\\nvirtually immune to the effects of noise and interference.\\\\n As the technologies of the microscale become better developed, more and more po-\\\\ntential designs for quantum computers, both conventional and unconventional, are likely\\\\nto arise. Additional technologies that could prove useful for the construction of quantum\\\\n\\\\n 4\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'de31f5a1-0290-45f3-b386-d02e3ed4c33c\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' logic devices include photonic crystals, optical hole-burning techniques, electron spin res-\\\\n onance, quantum dots, superconducting circuits in the quantum regime, etc. Since every\\\\n quantum degree of freedom can in principle participate in a computation one cannot a\\\\n priori rule out the possibility of using currently hard to control degrees of freedom such as\\\\nquark and gluon in complex nuclei to process information. Needless to say, most if not all\\\\nof the designs inspired by these technologies are likely to fail. There is room for optimism\\\\nthat some such quantum computer designs will prove practicable, however.\\\\n The preceding unconventional designs for quantum computers were based on existing,\\\\nexperimentally confirmed physical phenomena (except in the case of non-abelian anyons).\\\\nLet us now turn to designs based on speculative, hypothetical, and not yet verified phenom-\\\\nena. (One of the most interesting of these phenomena is large-scale quantum computation\\\\nitself: can we create and systematically transform entangled states involving hundreds or\\\\nthousands of quantum variables?) A particularly powerful hypothesis from the point of\\\\nview of quantum computation is that of nonlinear quantum mechanics.\\\\n The conventional picture of quantum mechanics is that it is linear in the sense that the\\\\nsuperposition principle is obeyed exactly. (Of course, quantum systems can still exhibit\\\\nnonlinear interactions between degrees of freedom while continuing to obey the superpo-\\\\nsition principle.) Experiment confirms that the superposition principle is indeed obeyed\\\\nto a high degree of accuracy. Nonetheless, a number of scientists including Weinberg have\\\\nproposed nonlinear versions of quantum mechanics in which the superposition principle\\\\nis violated. Many of these proposals exhibit pathologies such as violations of the second\\\\nlaw of thermodynamics or the capacity for superluminal communication. Despite such\\\\ntheoretical difficulties, it is still possible that quantum mechanics does indeed possess a\\\\nsmall nonlinearity, even if it currently seems unlikely. If a nonlinear operation such as\\\\nthat proposed by Weinberg can be incorporated in a quantum logic operation, then the\\\\nconsequences are striking: NP-complete problems can be solved easily in polynomial time\\\\n[17]. Indeed, NP-oracle problems and all problems in #P can be solved in polynomial time\\\\non such a nonlinear quantum computer.\\\\n A general proof of this result is given in [17], however, a simple argument for why\\\\nthis is so can be seen as follows. Suppose that it is possible to perform a non-unitary\\\\noperation on a single qubit that has a positive Lyapunov exponent over some region: i.e.,\\\\nsomewhere on the unit sphere there exists a line of finite extent along which application of\\\\nthe operation causes nearby points to move apart exponentially at a rate eλ∆θ proportional\\\\n 5\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'0777fa16-f0d4-4ad9-9877-a4abca0ba70f\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'to their original angular separation δθ. Now consider a function f (x) from N bits to one\\\\nbit. We wish to determine whether or not there exists an x such that f (x) = 1, and if\\\\nso, how many such x’s there are. Using the nonlinear operation with positive Lyapunov\\\\nexponent, it is straightforward to construct a mapping leaves a point on the exponentially\\\\nexpanding line (call this point |0〉) fixed if their are no solutions to the equation f (x) = 1,\\\\nand that maps the point to a nearby point cos(n/2N )|0〉 + sin(n/2N )|1〉 along the line\\\\nif there are exactly n solutions to the equation f (x) = 1. Repeated application of the\\\\nnonlinear map can be used to drive the points apart at an exponentional rate: eventually,\\\\nat a time determined by the number of qubits N , the number of solutions n, and the rate\\\\nof spreading λ, the two points will become macroscopically distinguishable, allowing one\\\\nto determine whether or not there is a solution and if there is, how many solutions there\\\\nare. The map f need only be applied once, and the amount of time it takes to reveal the\\\\nnumber of solutions is proportional to N .\\\\n The fact that nonlinear quantum mechanics allows the straightforward solution of\\\\nNP-complete and #P problems should probably be regarded as yet another strike against\\\\nnonlinear quantum mechanics. Whether or not quantum mechanics is linear is a question\\\\nto be resolved experimentally, however. In the unlikely event that quantum mechanics\\\\ndoes turn out to be nonlinear, all our problems may be solved.\\\\n Finally, let us turn our attention to hypothetical quantum Theories of Everything,\\\\nsuch as string theory. Such a theory must clearly support quantum computation since it\\\\nsupports cavity quantum electrodynamics and nuclear magnetic resonance. The obvious\\\\nquestion to ask is then, does a Theory of Everything need to support anything more than\\\\nquantum computation? So far as experimental evidence is concerned the answer to this\\\\nquestion is apparently No: we have no evident reason to doubt that the universe is at\\\\nbottom anything more than a giant, parallel, quantum information processing machine,\\\\nand that the phenomena that we observe and attempt to characterize are simply outputs\\\\nof this machine’s ongoing computation. Of course, just how the universe is carrying out\\\\nthis computation is likely to remain a question of great interest for some time.\\\\n To summarize: Computers are physical systems, and what they can do in practice and\\\\nin principle is circumscribed by the laws of physics. The laws of physics in turn permit a\\\\nwide variety of quantum computational devices including some based on nonconventional\\\\nstatistics and exotic effects. Modifications made to the laws of physics have the consequence\\\\nthat what can be computed in practice and in principle changes. A particularly intriguing\\\\n\\\\n 6\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'d315d076-2b6e-4ec4-ade1-e18fb6c09998\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'variation on conventional physics is nonlinear quantum mechanics which, if true, would\\\\nallow hard problems to be solved easily.\\\\n\\\\n 7\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'7cc3c174-6e77-4928-af58-522265cace2a\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'References\\\\n\\\\n1. P. Benioff, ‘Quantum Mechanical Models of Turing Machines that Dissipate No Energy,’\\\\nPhysical Review Letters, Vol. 48, No. 23, pp. 1581-1585 (1982)\\\\n2. D. Deutsch, ‘Quantum Theory, the Church-Turing Principle and the Universal Quantum\\\\nComputer,’ Proceedings of the Royal Society of London, A, Vol. 400, pp. 97-117 (1985).\\\\n3. R.P. Feynman, ‘Quantum Mechanical Computers,’ Optics News, Vol. 11, pp. 11-20\\\\n(1985); also in Foundations of Physics, Vol. 16, pp. 507-531 (1986).\\\\n4. S. Lloyd, ‘A Potentially Realizable Quantum Computer,’ Science, Vol. 261, pp. 1569-\\\\n1571 (1993).\\\\n5. J.I. Cirac and P. Zoller, ‘Quantum Computations with Cold Trapped Ions,’ Physical\\\\nReview Letters, Vol. 74, pp. 4091-4094 (1995).\\\\n6. Q.A. Turchette, C.J. Hood, W. Lange, H. Mabuchi, H.J. Kimble, ‘Measurement of\\\\nConditional Phase Shifts for Quantum Logic,’ Physical Review Letters, Vol. 75, pp. 4710-\\\\n4713 (1995).\\\\n7. C. Monroe, D.M. Meekhof, B.E. King, W.M. Itano, D.J. Wineland, ‘Demonstration of\\\\na Fundamental Quantum Logic Gate,’ Physical Review Letters, Vol. 75, pp. 4714-4717\\\\n(1995).\\\\n8. D.G. Cory, A.F. Fahmy, T.F. Havel, ‘Nuclear Magnetic Resonance Spectroscopy: an\\\\nexperimentally accessible paradigm for quantum computing,’ in PhysComp96, Proceedings\\\\nof the Fourth Workshop on Physics and Computation, T. Toffoli, M. Biafore, J. Le˜ao, eds.,\\\\nNew England Complex Systems Institute, 1996, pp. 87-91.\\\\n9. N.A. Gershenfeld and I.L. Chuang, ‘Bulk Spin-Resonance Quantum Computation,’\\\\nScience, Vol. 275, pp. 350-356 (1997).\\\\n10. P. Shor, ‘Algorithms for Quantum Computation: Discrete Log and Factoring,’ in\\\\nProceedings of the 35th Annual Symposium on Foundations of Computer Science, S. Gold-\\\\nwasser, Ed., IEEE Computer Society, Los Alamitos, CA, 1994, pp. 124-134.\\\\n11. R.P. Feynman, ‘Simulating Physics with Computers,’ International Journal of Theo-\\\\nretical Physics, Vol. 21, pp. 467-488 (1982).\\\\n12. S. Lloyd, ‘Universal Quantum Simulators,’ Science, Vol. 273, pp. 1073-1078 (1996).\\\\n13. L.K. Grover, ‘Quantum Mechanics Helps in Searching for a Needle in a Haystack,’\\\\nPhysical Review Letters, Vol. 79, pp. 325-328 (1997).\\\\n14. D. Deutsch, A. Barenco, A. Ekert, ‘Universality in Quantum Computation,’ Proceed-\\\\nings of the Royal Society of London A, Vol. 449, pp. 669-677 (1995).\\\\n\\\\n 8\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'a9610b7b-a167-4f21-a917-3626fbddde3b\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Unconventional Quantum Computing Devices\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' 15. S. Lloyd, ‘Almost Any Quantum Logic Gate is Universal,’ Physical Review Letters,\\\\n Vol. 75, pp. 346-349 (1995).\\\\n 16. S. Lloyd, ‘Fermionic Quantum Computers,’ talk delivered at the Santa Barbara work-\\\\n shop on Physics of Information, November 1996.\\\\n 17. D. Abrams and S. Lloyd, to be published.\\\\n 18. J. Preskill et al., to be published.\\\\n19. Yu. Kitaev, to be published.\\\\n20. J. Preskill et al., to be published.\\\\n21. S. Lloyd et al. to be published.\\\\n\\\\n 9\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}]}, {\\'documents\\': [{\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'278bd226-32be-4488-a970-323f579ee8e1\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'arXiv:1707.02094v1 [quant-ph] 7 Jul 2017\\\\n\\\\n Quantum-dot based photonic quantum networks\\\\n Peter Lodahl\\\\n Niels Bohr Institute, University of Copenhagen, Blegdamsvej 17, DK-2100\\\\n Copenhagen, Denmark\\\\n E-mail: lodahl@nbi.ku.dk\\\\n\\\\n Abstract. Quantum dots embedded in photonic nanostructures have in recent years\\\\n proven to be a very powerful solid-state platform for quantum optics experiments. The\\\\n combination of near-unity radiative coupling of a single quantum dot to a photonic\\\\n mode and the ability to eliminate decoherence processes imply that an unprecedent\\\\n light-matter interface can be obtained. As a result, high-cooperativity photon-\\\\n emitter quantum interfaces can be constructed opening a path-way to deterministic\\\\n photonic quantum gates for quantum-information processing applications. In the\\\\n present manuscript, I review current state-of-the-art on quantum dot devices and their\\\\n applications for quantum technology. The overarching long-term goal of the research\\\\n field is to construct photonic quantum networks where remote entanglement can be\\\\n distributed over long distances by photons.\\\\n\\\\n 1. Introduction\\\\n\\\\n The ultimate vision of photonic quantum technology is to construct a complex quantum\\\\n network of stationary quantum nodes connected by flying photons in a fully quantum\\\\n way, i.e. quantum entanglement may become distributed. Such a new photonic\\\\n paradigm would have novel applications within secure quantum communication and\\\\n is proposed as a way of scaling up quantum computers. It is popularly referred to as the\\\\n ’quantum internet’ [1] and has been rooted in the atomic physics community where an\\\\n impressive proof-of-concept elementary two-node quantum network has been achieved\\\\n [2]. Solid-state alternatives to atomic single-photon emitters are attractive, since unlike\\\\n atoms they do not require complex laser cooling and trapping techniques. On the\\\\n other hand solid-state systems are often considered to be ’noisy’ in the sense that many\\\\n potential decoherence processes may deteriorate quantum properties. Remarkably, self-\\\\n assembled quantum dots (QDs) emitting single photons in the optical domain, notably\\\\n InGaAs QDs embedded in GaAs semiconductors, have matured dramatically within\\\\n the last few years. By systematically studying and combating the relevant decoherence\\\\n processes [3] impressive coherence has been demonstrated including near-perfect single-\\\\n photon indistinguishability (above 98%) of two subsequently emitted photons [4, 5] and\\\\n transform-limited emission lines [6]. Combined with the ability to dramatically enhance\\\\n light-matter interaction in photonic nanostructures [7], this enables near-deterministic\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'c6fdc95e-eabb-4b5e-a3c0-4c315d4c8f59\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 2\\\\n\\\\nsingle-photon sources with an internal collection efficiency exceeding 98 % [8]. This\\\\nmajor progress entails that QD single-photon sources are now gradually outperforming\\\\nthe traditional approaches based on atoms or spontaneous parametric down-conversion.\\\\nQD sources benefit from fast operation speeds, excellent stability and brightness, as well\\\\nas the potential scalability to multiple single photons and emitters. Notably, a number of\\\\nimpressive experiments have recently been implemented with QDs that have not been\\\\naccomplished on other platforms. They include the experimental demonstration of a\\\\ndeterministic entangled cluster state of strings of photons [9], boson sampling with so far\\\\nfive photons [10], the detection of squeezed light correlations in resonance fluorescence\\\\n[11], and the demonstration of an on-demand entangled photon source with higher\\\\nthan 90% fidelity [12]. Time is now to build on these and other achievements in order\\\\nto scale QD photonic quantum technology and construct large and complex quantum\\\\narchitectures for quantum-information processing applications.\\\\n The present manuscript reviews the current state-of-the-art on quantum photonics\\\\nbased on QD photon emitters towards the overarching goal of constructing photonic\\\\nquantum networks. At present, a number of basic functionalities have been\\\\nsuccessfully demonstrated with generally impressive performance. These may constitute\\\\nfundamental building blocks of photonic quantum networks. In that sense a current\\\\nmajor challenge for theoretical quantum physicist is to develop resource efficient\\\\narchitectures tailored to the specific quantum hardware available, i.e., addressing how\\\\nthe basic ’puzzle pieces’ can be combined. Figure 1 illustrates the ’puzzle pieces’ already\\\\nbeing developed for the QD platform, and that will be considered here. They include\\\\nsingle-photon sources, single-photon nonlinearity, photonic circuitry, efficient-coupling\\\\nto optical fibers, on-chip single-photon detectors, and multi-emitter coupling.\\\\n QDs enable two different types of quantum resources: they may be employed\\\\nas a source of single photons or alternatively a single spin trapped in a QD may be\\\\nmanipulated as a qubit. Figure 2 illustrates these two approaches. In the former case a\\\\nsingle electron-hole pair is created in a QD by either optical excitation or by controllably\\\\ntunneling carriers into the QD with the application of an electric field. The electron-\\\\nhole pair subsequently recombines whereby a single photon is emitted by spontaneous\\\\nemission within a lifetime of typically 1 ns for a QD in a bulk medium. In the latter\\\\napproach a single carrier (electron or hole) is prepared in the QD by tunneling. The\\\\ncarrier spin is coupled to light since a photon may subsequently be absorbed by the QD\\\\ncreating a negatively (positively) charged exciton for the case of an initial electron (hole).\\\\nDepending on the operational condition, the spin may have a coherence time of up to\\\\n1 μs (for holes) [13, 14], which means that in principle many quantum operations can\\\\nbe carried out with fast optical control techniques before decoherence sets in. For some\\\\nquantum-information applications, however, longer spin coherence times are required,\\\\ne.g., in matter based quantum-repeater architectures. To this end, the QD platform\\\\nmust be interfaced with long-lived quantum memories based on, e.g., defect centers\\\\nin diamond or atomic ensembles [15]. Such hybrid quantum network architectures are\\\\noutside the scope of the present Review.\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'a19b0d3b-ebad-4d36-a615-6a4ee63a39ad\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 3\\\\n ~l ~\\\\n Nonlinearity Processing\\\\n wn\\\\n Fiber Out-coupling Single photons Detection Dipole-dipole interaction\\\\nFigure 1: Illustration of basic functionalities that can be implemented with QDs and\\\\nphotonic nanostructures. Left panel: efficient outcoupling taper sections from photonic\\\\nnanostructures to optical fibers can be engineered to obtain highly efficient outcoupling\\\\nof single photons. Center panel: a single QD in nanophotonic waveguide or cavity can be\\\\nused as a highly efficient and coherent source of single photons. Two sources are shown\\\\nin order to illustrate potential scalability of the approach. Right panel: a single QD\\\\nefficiently coupled to a waveguide may be employed as a single-photon nonlinearity\\\\n(upper left), complex photonic circuits may be constructed on a photonic chip for\\\\nquantum processing of photons (upper right), single-photon superconducting detectors\\\\nmay be implemented on-chip for highly efficient detection (lower right), and multiple\\\\nQDs may be coupled by engineering the dipole-dipole interaction in a nanophotonic\\\\nwaveguide. The figure is a courtesy of Sahand Mahmoodian.\\\\n 2 2\\\\n Ia) hole k(b)\\\\n electron M MM\\\\nFigure 2: Photon and spin qubits generated with QDs. The light grey areas illustrate\\\\nthe regions of InAs embedded in dark grey GaAs regions thereby creating an energy\\\\nwell potential for both electrons and holes. (a) An electron-hole pair trapped in the\\\\nQD may recombine by emitting a single photon. (b) A single electron (or hole) trapped\\\\nin the QD constitutes a spin qubit (orange arrow) that may be manipulated by optical\\\\nmethods.\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'54241ad6-863a-4e89-b9c2-25ea50cede61\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 4\\\\n\\\\n2. Single-photon sources\\\\n\\\\nA QD efficiently coupled to a nanophotonic cavity or waveguide offers a promising\\\\nroute towards a deterministic single-photon source. Early pioneering work showed that\\\\nPurcell enhancement in an optical cavity may be employed to both improve brightness\\\\nand coherence of the source [16]. Ideally a useful single-photon source emits an optical\\\\npulse containing a single photon in a useful optical mode (e.g., into an optical fiber)\\\\nevery time the QD is triggered by either an optical or electrical pulse. Furthermore,\\\\nmost applications in quantum-information processing require that the emitted photons\\\\nare fully coherent, which entails that decoherence processes occurring on a time scale\\\\nof the radiative decay of the QD must be eliminated. Thus, three major figures-of-\\\\nmerit need to be optimized: i) the single-photon purity, ii) the single-photon coupling\\\\nefficiency, and iii) the indistinguishability of the emitted photons.\\\\n Re i): The discrete level structure of self-assembled QDs having widely separated\\\\noptical transitions implies that they are capable of emitting high-purity single photons\\\\n[17]. This is gauged in a Hanbury Brown - Twiss (HBT) correlation experiment by\\\\nrecording the multi-photon emission probability. By using samples with a low QD\\\\ndensity and implementing (quasi)-resonant excitation to selectively excite only a single\\\\nQD, excellent single-photon purity can be obtained at the level of g(2)(0) ≤ 0.1%, cf.\\\\nFig. 3.\\\\n Re ii): The overall coupling efficiency determines the brightness of the single-photon\\\\nsource and is comprised of several factors [7]: the excitation and single-photon emission\\\\nprobability of the QD, the efficiency with which the emitted photons are channeled to a\\\\nsingle mode (the β-factor), and the transfer efficiency to a low-loss propagating mode,\\\\ne.g., an optical fiber. Significant progress has been reported on all three engineering\\\\ntasks: resonant π-pulse excitation allows deterministically preparing a single exciton in\\\\na QD [18] and electrically gated structures eliminate blinking between different exciton\\\\ncomplexes [19]. Embedding QDs in photonic nanostructures allows reaching a near-\\\\nunity β-factor, which has been obtained in nanophotonic waveguides [8, 20] and cavities\\\\n[4]. Finally, a variety of approaches and designs can be implemented for transferring\\\\nthe collected photons to an optical fiber using, e.g., tailored gratings for coupling light\\\\nguided on planar structures vertically off the chip, or waveguide taper sections designed\\\\nto couple directly to a fiber either by direct or evanescent coupling [21, 22, 23].\\\\n Re iii): The indistinguishability of an emitted photon is determined by the amount\\\\nof decoherence taking place within the emitter decay time. With a typical decay time\\\\nof nanoseconds, the primary decoherence source is coupling of the QD to phonons\\\\nand potentially photon jitter induced by relaxation processes originating from non-\\\\nresonant excitation schemes [24]. The latter may be overcome by strict resonant\\\\nexcitation. Phonon interactions at the contrary are unavoidable and lead to two\\\\neffects: wide phonon sidebands and residual broadening of the zero-phonon line. The\\\\nformer contribution (containing typically about 10 % of the the total emission at\\\\nlow temperatures [7]) can readily be filtered away either by an external filter thereby\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'de71718d-112b-478e-b755-5c7d40825e52\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 5\\\\n\\\\nreducing the overall efficiency or more favorably by implementing narrow-band Purcell\\\\nenhancement. Hence the broadening of the zero-phonon line remains the fundamental\\\\ndecoherence mechanism. An in-depth theoretical study of this process including the\\\\nrole of the dimensionality of the photonic nanostructure can be found in Ref. [25].\\\\nImportantly a clear route to indistinguishable photons with near-unity visibility can be\\\\nlaid out, which has also been confirmed experimentally [4, 5, 26].\\\\n The previous discussion illustrates that QDs are capable of producing a truly on-\\\\ndemand source of single photons by implementing, in a single device, the functionalities\\\\ndemonstrated so far only in different experiments. It is likely that such a source will\\\\nbe developed experimentally soon. In particular, it is remarkable that decoherence\\\\nprocesses can be overcome in a solid-state system to such an extent that near-unity\\\\nindistinguishability between subsequently emitted photons from the same QD can be\\\\nobtained. Next step is to convert a single QD source generating photons as ”pearls\\\\non a string” into a de-multiplexed source delivering many identical single photons\\\\nsimultaneously in individual modes. Such a source can be constructed by cascading\\\\nelectro-optical switches and coupling the photons into different optical fibers in order\\\\nto overcome the time delay between subsequently emitted photons, cf. Fig. 4(a).\\\\nTo this end, the demonstration of near-transform-limited QD emission lines [6] has\\\\nthe remarkable consequence that a single QD can emit thousands of indistinguishable\\\\nphotons thereby providing a huge quantum resource. Indeed recent demonstration of\\\\nindistinguishability exceeding 90 % between two photons separated by more than 1000\\\\nemission events has proven this explicitly [27].\\\\n\\\\n3. Integrated quantum photonics\\\\n\\\\nAs detailed in the previous section, currently the most reliable approach to generate\\\\nmultiple single photons utilizes a single integrated QD source where the photons\\\\nare coupled off-chip to an optical fiber, and highly-efficient bulk optical components\\\\nimplemented for switching and routing, cf. Fig. 4(a). Nonetheless, the potential benefits\\\\nof integrating functionalities on-chip are many in terms of stability, ease of operation,\\\\nlow loss, and speed [28]. Figure 4(b) shows an architecture for an integrated de-\\\\nmultiplexed source of single photons based on a single QD in a planar nanophotonic\\\\nwaveguide platform. To implement this scheme requires the development of high-\\\\nefficiency and fast switches, and multi-port waveguide-fiber interfaces. The former\\\\nrequires the development of new devices and functionalities on-chip where so-far the\\\\nmost common approach for reconfigurable circuitry has been the application of thermo-\\\\noptic phase shifters [29]. De-multiplexing requires fast switches, and two promising\\\\napproaches include electro-optical modulation implemented directly in the host material\\\\nof the QD (i.e. GaAs) [30, 31] or electro-mechanical coupling [32].\\\\n The crucial parameter determining the size of the photonic resource that can be\\\\ngenerated with a de-multiplexed single-photon source is the overall efficiency, including\\\\nswitching, propagation, and coupling efficiencies. Consequently the rate of N-photon\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'5982b6b2-feb7-48e0-9d25-1a0c89baaf44\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 6\\\\n 15 pairs taper WG\\\\n ~Fiber\\\\n pairs\\\\n Tum\\\\n Wch\\\\n HBT\\\\n 3 0.8 = Channel\\\\n 1 WG\\\\n Fiber taperWG\\\\n 0.4-\\\\n 0.2 300 r 140 nm\\\\n \"37.5 25.0 -12.5 Delay (ns) 12.50,0 25,0 37.5 a\\\\n 1 0.2 Az\\\\n 3 060.81.0 HOMpol:\\\\n 1 0.8 inputNanobeam waveguidePC 90.10 samplecryostat 0.8\\\\n 0.6 0.6\\\\n 0.0 0.4 0.4 ]\\\\n ] 1.00.8\\\\n 0.6 NU pOlvIpot 1 0.2 Photonic-crystal waveguide 0.2\\\\n 0.4\\\\n 0.2 1 0.60.4\\\\n 0.2 0.4\\\\n 0.6\\\\n 0.2\\\\n Time delay (ns) 850 900 950 1000\\\\n Wavelength (nm)\\\\nFigure 3: Purity, indistinguishability, and out-coupling efficiency of QD single-\\\\nphoton sources. Left column: Examples of HBT and Hong-Ou-Mandel (HOM)\\\\nindistinguishability measurements by implementing resonant excitation on a QD in\\\\na micropillar cavity (upper figure shows the cavity structure). g(2)(0) = 0.009 and\\\\na single-photon indistinguishability of 96.4% is extracted from the two sets of data.\\\\nFigures reproduced from Ref. [5]. Right column: Examples of devices made for out-\\\\ncoupling single photon from a QD in a planar nanophotonic waveguide to an optical\\\\nfiber with high efficiency by tailored evanescent coupling. The upper panel is reproduced\\\\nfrom Ref. [21]. Lower panel: application of this method to couple out single photons\\\\nfrom high β-factor nanophotonic waveguides, i.e., nanobeam waveguides and photonic-\\\\ncrystal waveguides. By recording the reflection and transmission spectrum of the device,\\\\na chip-to-fiber coupling efficiency of > 80% is obtained. Data reproduced from Ref. [23].\\\\n\\\\ngeneration is RN = Rpump ×ηN /N , where η is the overall transmission efficiency from the\\\\nsource to the fiber and Rpump is the repetition rate of the excitation laser. Exemplarily\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'22f52865-e757-49a9-8244-cad99bf66572\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 7\\\\n PC (b) Electrically-controlled\\\\n switches\\\\n de-muitipieett QD 8-98% Array of delay fibers\\\\n 2\\\\n Oei\\\\n Photonic crystal\\\\n ultte ~fow-Ioss deleco e waveguide\\\\n photonic circuit\\\\nFigure 4: Illustrations of schemes for de-multiplexing a single-photon source. (a)\\\\na five-photon source obtained by cascading four Pockels cells (PCs) and polarizing\\\\nbeamsplitters to couple subsequently emitted photons from the QD into different optical\\\\nfibers with different delay lengths. This de-multiplexed source is applied for proof-\\\\nof-concept boson sampling. Figure reproduced from Ref. [10]. (b) Architecture\\\\nof an integrated de-multiplexed source based on a QD coupled with high efficiency\\\\nto a photonic-crystal waveguide. The emitted photons are transferred to dielectric\\\\nwaveguides and subsequently routed by electrical switches and coupled off-chip to\\\\ndifferent fiber delays in order to compensate for the time delay in-between photons.\\\\nThe figure is a courtesy of Leonardo Midolo.\\\\n\\\\nconsidering η = 50% and Rpump = 80 MHz (corresponding to the repetition rate of a\\\\nTi:Sapph. laser) corresponds to a 10-photon generation rate of R10 ∼ 8 kHz, which\\\\nillustrates the promising prospects of this approach while posing clear benchmarks for\\\\nthe efficiency of the applied switching and coupling technology.\\\\n Integrating a de-multiplexed source on a chip is one immediate goal. Another\\\\nimportant task is to develop complex integrated tunable photonic circuits that can\\\\nprocess the generated single photons leading to applications for quantum simulations\\\\n[33]. Current state-of-the-art of this technology is a six-mode reprogrammable circuit\\\\ncapable of implementing universal high-fidelity quantum gates [29]. Considerable effort\\\\nis directed to the scaling of these circuits in working towards a fully integrated system\\\\nwhere photon source and processing circuit would be implemented on a single photonic\\\\nchip. However, implementing on-chip the relatively long optical delays (typically tens of\\\\nnanoseconds) required to interfere subsequently emitted photons, poses a challenge for\\\\nthis approach in requiring slow-light optical buffers. Consequently at present it seems\\\\nmost realistic to consider approaches based on two separate chips, connected by optical\\\\nfibers, where one chip constitutes the source and the other chip the quantum processor.\\\\n The operation wavelength of a quantum photonics processor is an essential\\\\nparameter. The preferred operation wavelength is within the telecom C-band (1.55 μm),\\\\nwhere advanced high-performance optical components and fiber technology developed\\\\nfor telecom industry are present. The natural emission wavelength of self-assembled high\\\\nquality QDs is around 950 nm and while significant efforts are taken to develop QDs in\\\\nthe telecom bands as well [34, 35] the excellent performance in terms of coherence and\\\\nefficiency required for quantum technology has not yet been achieved. An alternative\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'6005404e-7547-45e3-b282-52c7be36abcf\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 8\\\\n\\\\napproach employs frequency down-conversion of single photons from the near-infrared\\\\nto the telecom band. Noise free conversion of single photons from a QD with an\\\\nefficiency exceeding 30% has been demonstrated [36], which could be further increased by\\\\nimproving the in- and out-coupling efficiency through the nonlinear conversion crystal.\\\\nThe two-chip approach alluded to above has the asset that the frequency conversion\\\\nstep could naturally be incorporated in between the source chip and the processing\\\\nchip. Furthermore, implementing frequency conversion of QD photons also has another\\\\nadvantage, since it provides a way of overcoming spectral inhomogeneities of photons\\\\nemitted by different QDs by transducing them to the same wavelength in the C-band\\\\nwith the implementation of a tunable pump laser. This could prove a powerful way of\\\\nscaling up QD systems to couple multiple emitter qubits (e.g., spins) in addition to the\\\\nmultiple photon qubits already considered.\\\\n Finally, another essential requirement is the ability to detect optical photons with\\\\nvery high efficiency. Very significant progress has been obtained in recent years with the\\\\ndevelopment of superconducting nanowire single-photon detectors [37], where the single-\\\\nphoton detection efficiency today approaches 100% and the speed is compatible with QD\\\\nsources. Importantly these detectors can naturally be integrated on the planar GaAs\\\\nplatform containing QDs [38], which means that photon source, circuit, and detection\\\\ncould potentially be integrated on a single chip, which may pave the way to ultimately\\\\nlow-loss photonic quantum nodes.\\\\n\\\\n4. Single-photon nonlinearity\\\\n\\\\nThe previous sections concerned the generation of single photons and their subsequent\\\\nquantum interference in photonic circuits. Many quantum-information applications\\\\nof photons require generating photon-photon interactions. Generally photons interact\\\\nweakly meaning that standard nonlinearities are typically too weak to be operational\\\\nat the level of single photons. However, the efficient coupling of single QDs to photonic\\\\nnanostructures imply that such a system may be exploited to mediate a giant nonlinear\\\\nresponse operational at the level of single photons. Two different approaches can be\\\\ntaken to reach a giant nonlinearity with a QD or any other quantum emitter: the QD\\\\ncan be strongly coupled to a cavity and the nonlinearity of the Jaynes-Cummings ladder\\\\nexploited [39, 40], or a QD very efficiently coupled to a single optical mode in a cavity\\\\n[41, 42] or waveguide [43] may be used as a saturable nonlinearity. The latter approach\\\\nis beneficial for ease of operation, since it is less sensitive to the exact tuning of the\\\\nwavelength of the QD. Furthermore, the saturable QD nonlinearity generally can be\\\\noperated at a weaker incident photon flux [43]. The key operational principle of the QD\\\\nnonlinearity is as follows: a narrow-band pulse containing a single photon is reflected\\\\nfrom a QD in a waveguide due to destructive interference of forward scattering and the\\\\nincoming field. The efficient coupling (high β-factor) and coherent interaction, which\\\\ncan be achieved with QDs, implies that the interaction between the photon and the\\\\nQD is deterministic. Since a two-level emitter can only scatter one photon at a time,\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'c1b95287-4dfa-411d-8269-8450b9dac1db\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 9\\\\n\\\\ntwo photons have an increased probability to be transmitted past the QD leading to a\\\\nphoton sorting process. From an experimental point of view this type of nonlinearity\\\\nis attractive since no active control over the quantum state of the QD is required,\\\\ni.e., the QD is merely exploited as a passive scatterer. On the other hand, the photon\\\\nsorting is not ideal even for a perfect coupling efficiency. This implies that while a single-\\\\nphoton component of a pulse may be deterministically reflected, two- and higher-photon\\\\ncomponents are only partially transmitted and in this process entanglement is generated\\\\nbetween the two photons [44]. One approach of overcoming this limitation and obtain\\\\ndeterministic photon sorting has been suggested by combining the QD nonlinearity with\\\\na nonlinear spatio-temporal mode selector [45]. Such an approach may be applied for\\\\nconstructing a resource-efficient Bell-state analyzer [44, 45], cf. Section 7. It should be\\\\nmentioned that having a three-level emitter with coherent control of the level populations\\\\nin the waveguide opens a range of additional opportunities. Such level schemes can be\\\\nimplemented with charged QDs, cf. the description in the next section. One exciting\\\\nproposal is that of a photonic transistor controlled by only a single photon [46].\\\\n\\\\n5. Spin-photon interfaces\\\\n\\\\nIntroducing a single spin in a QD leads to a range of additional opportunities. It grants\\\\naccess to two separate ground states (spin up or down) that can be exploited as a\\\\nquantum memory to encode a qubit. Either single holes or electrons can be employed\\\\nand may be deterministically prepared by controlling the tunnel barriers in electrically\\\\ncontacted semiconductor structures. Various level structures can be implemented by the\\\\nuse of Zeeman tuning with a magnetic field, and spin initialization, manipulation, and\\\\nread-out can be achieved rapidly with short optical pulses (typically in the nanosecond\\\\nrange), cf. Ref. [19] for a detailed review of the physics of single spins in QDs.\\\\nThe spin lifetime can be in the range of milliseconds or longer in a large magnetic\\\\nfield, which is determined by phonon-mediated spin relaxation processes. However,\\\\nin quantum applications the coherence time of the spin is the essential parameter.\\\\nA coherence time of up to T 2∗ > 460 ns has been reported for hole spins that has\\\\nthe advantage compared to electrons that they do not suffer from the Fermi contact\\\\nhyperfine interaction with nuclear spins [14]. Importantly the spin coherence time can\\\\nbe several orders of magnitude longer than the spin manipulation time, which means\\\\nthat multiple quantum gate operations could be implemented within the spin coherence\\\\ntime. Unravelling spin decoherence mechanisms in QDs is a research topic in itself and is\\\\noutside the scope of the present review. For a thorough discussion, see Refs. [19, 47]. It\\\\nis anticipated that further research efforts will enable increasing the spin coherence time\\\\nfurther. Nonetheless, it seems unlikely that it will be possible to extend the coherence\\\\ntime by many orders of magnitude in present QD systems. In particular, long-range\\\\nquantum repeater protocols would require millisecond range quantum memories. This\\\\nentails a hybrid approach where the single photons generated by QDs are interfaced\\\\nwith long-lived quantum memories, e.g., vacancy centers in diamond [48], ions [49], or\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'093afd39-07a0-4589-a0c9-eb679ce77c2f\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' Quantum-dot based photonic quantum networks 10\\\\n\\\\n potentially mechanical systems.\\\\n Considering a charged exciton in an external magnetic field, various four-level\\\\n emitter schemes can be engineered with two ground states corresponding to either\\\\n (pseudo)spin up or down of the electron (hole). The recombination of a charged exciton\\\\n leads to the emission of polarized photons, where the polarization of the photon is\\\\n correlated with the resulting spin state. In this way entanglement between the QD\\\\n spin and the polarization of the emitted photon can be generated [13, 50] where\\\\n in general hyper-entanglement between spin, polarization, and frequency is obtained.\\\\n Subsequently the information in one of the variables can be erased to create a Bell state.\\\\n This has been the resource enabling quantum teleportation between a single photon and\\\\n a solid-state spin [51]. A further extension of this experiment was the demonstration of\\\\n heralded entanglement between two spatially separate hole spins in QDs [52], cf. Fig. 5\\\\n for the experimental layout.\\\\n Another possibility is to couple QD spins directly to photon path by exploiting\\\\n the concept of chiral light-matter interaction [53]. By tailoring the local electric field\\\\n in a nanophotonic waveguide to match the in-plane circular dipole moment of one of\\\\n the charged exciton transitions, it is possible to induce deterministic directional photon\\\\n emission [54], i.e. σ± transitions emit in different directions. This effect was used\\\\nto demonstrate an interface between QD spin and the photon path [55]. Based on\\\\nthis functionality a photonic quantum network architecture was put forward and its\\\\nfeasibility evaluated for QDs in nanophotonic waveguides [56], cf. Fig. 5.\\\\n\\\\n6. Coupling of multiple quantum dots\\\\n\\\\nWe have seen in previous sections how a single QD deterministically coupled to a single\\\\noptical mode may be exploited as a source of multiple photonic qubits potentially\\\\ngenerating a large quantum resource. It is natural to consider the option of scaling\\\\nfurther up by coupling multiple QDs. This is generally a demanding task, as it is\\\\nthe case for any solid-state optical emitter, since inhomogeneous broadening of a QD\\\\nensemble (typically at the level of THz, which should be compared to the sub-GHz\\\\nhomogenous linewidth of a QD) implies that significant spectral mismatch would need\\\\nto be overcome. Various tuning methods of QDs have been implemented including\\\\nelectric-field tuning [57] and strain tuning [58] that needs to be implemented locally to\\\\neach individual QD. Eventually not only the central emission frequency of the different\\\\nemitters must be controlled, but the complete photon pulses need to be matched in\\\\norder to faithfully couple multiple QDs. An important figure-of-merit of the success is\\\\ngauged in a quantum-interference experiment between two separate QD single-photon\\\\nsources, where an interference visibility of 93% has been reported in a weak-excitation\\\\nregime [59], which is a highly encouragin result for this approach.\\\\n Given the ability to radiatively couple two or more QDs, a photonic nanostructure\\\\ncan be constructed to enhance and tailor the coupling. Multiple QDs coupled by\\\\ndipole-dipole interaction lead to the formation of collective quantum states that can\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'39ce6fe7-fe46-40b9-9d03-35a978fc7363\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 11\\\\n Entanglement and\\\\n non-local measurement\\\\n Spin preparation and Spin preparation and local (a) Chiral element\\\\n local measurements (QDI) Ti Sapphiremeasurements (QD2)\\\\n laser\\\\n Diode las 0 +\\\\n Diode lasers /1)\\\\n EOM (b) Network node\\\\n EOMs EOMs\\\\n Intranode parity measurement Internode entanglement\\\\n BSI Detector 0 Detector\\\\n Emitter 0\\\\n Fiber Emitter\\\\n Single photon Communication photon\\\\n source\\\\n N Gratings BS2\\\\n (c) Linked Nodes\\\\n filters\\\\n Single-photon detectors Photon routing\\\\n QDI OD2\\\\n 5 m\\\\nFigure 5: Left panel: experimental setup employed for the generation of remote\\\\nentanglement between two QD hole spins in two different cryostats separated by\\\\n5 m. Figure reproduced from Ref. [52]. Right panel: Architecture of a photonic\\\\nquantum network based on chiral photon-emitter elements (a) embedded in on-chip\\\\ninterferometers constituting fundamental quantum nodes (b). The quantum nodes are\\\\nlinked together by photons for remote entanglement distribution (c). Figure reproduced\\\\nfrom Ref. [56].\\\\n\\\\nhave sub- or super-radiant character. In photonic nanostructures, the dipole-dipole\\\\ninteraction can be significantly extended beyond the behavior found in a homogenous\\\\n(3D) medium, where the coupling falls off rapidly with distance d (scales as d−3). In\\\\na 1D waveguide with radiative coupling to the waveguide as the dominating process,\\\\ndipole-dipole interaction may be infinitely extended, i.e. in practice it is limited by\\\\nthe absorption or scattering loss of the guided mode. Controlling the distance between\\\\nmultiple emitters enables the switching between sub- and super-radiant behavior. Apart\\\\nfrom the fundamental interest, the long-range dipole-dipole coupling may be exploited\\\\nto construct quantum gates between stationary qubits [60]. Introducing chiral coupling\\\\nopens new opportunities by the ability to engineer the directional coupling between\\\\nemitters. This may lead to novel opportunities for quantum simulators and for entering\\\\nnew regimes of coupled photons and emitters. For instance directional coupling has been\\\\nfound to lead to the formation of quantum dimers in the steady state of the system [61].\\\\nAnother approach to scaling to multiple emitters exploits tunnel coupling between QDs\\\\ngrown in close mutual vicinity. With such an approach advanced level structures can\\\\nbe engineered in these QD molecules where it has been demonstrated that the spin\\\\ncoherence time can be significantly extended [62].\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'5516db24-13d0-408f-a09f-df9415a927b9\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 12\\\\n\\\\n7. Quantum network architectures\\\\n\\\\nWe have reviewed various basic quantum functionalities and hardware that can be\\\\nimplemented with the use of QDs embedded in modern nanophotonic structures. It\\\\nis a pertinent task, to consider what applications and actual architectures can be\\\\nimplemented with these quantum resources and how to make them most resource\\\\nefficient. This section briefly highlights some of the opportunities and future directions.\\\\nA general observation, as discussed in detail above, is that QD systems can generate\\\\nmany single-photon qubits, while the scaling up to many matter qubits is expected to\\\\nbe much more challenging. It is important to have these general guidelines in mind\\\\nwhen designing resource efficient quantum network architectures that is tailored to the\\\\nquantum hardware based on QDs.\\\\n As discussed in the present Review, QDs in nanophotonic structures are very well\\\\nsuited for generating highly coherent single photons on demand at a high repetition\\\\nrate. Such a source can generate thousands of indistinguishable photons [27] that if\\\\nefficiently routed could be an important photonic resource for quantum simulations [33].\\\\nFurthermore, recent developments on how to efficiently fuse photonic qubits with linear\\\\noptics have the renewed excitement on how to construct single-photon based quantum\\\\ncomputing [63].\\\\n Introducing a single-photon nonlinearity to the toolbox opens for a range of\\\\nadditional possibilities. The most simple case is that of a two-level emitter, and here\\\\ndistortion of the optical pulses associated with photon-emitter bound state contributions\\\\nhas been interpreted as essentially a ”no-go theorem” for quantum computation [64].\\\\nOne work around, however, has been identified where the pulse distortion is actually\\\\nexploited such that one- and two-photon pulses are scattered into orthogonal spectral-\\\\ntemporal modes that can subsequently be separated, e.g., by nonlinear frequency\\\\nconversion [45]. With such a functionality all path-encoded photonic Bell states can\\\\nbe deterministically measured exploiting only four non-linear interaction process, linear\\\\noptics, and photon detection, cf. Fig. 6. Based on a Bell-state analyzer, quantum\\\\ncomputing can in principle be implemented by teleportation based gates and linear\\\\noptical quantum computing [65]. Alternatively, a photonic CZ gate can be directly\\\\nbased on the two-level emitter nonlinearity. However, in this case the pulse distortion\\\\nintroduced by photon scattering has to be undone by scattering an additional time on\\\\nthe emitter. However, since this scattering process is not time symmetric the photon\\\\npulse would need to be reverted in time in between the two scattering process. Such a\\\\ntime reversal of the photon pulses could in principle be obtained by storing, reverting,\\\\nand releasing photon pulses in a gradient-echo quantum memory.\\\\n Having access to a 3-level Λ-scheme with two stable ground states leads to many\\\\nmore opportunities since now the emitter can store a qubit for extended times. Coherent\\\\ncontrol of the qubit state enables implementing a Duan-Kimble CNOT gate for photons\\\\nwhere two subsequently interacting photons are entangled by their successive interaction\\\\nwith the same emitter [66]. Another related approach employs single-photon Raman\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'d1065b19-dd0a-4177-9589-f30e9ba2ae04\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 13\\\\n 1 photon\\\\n TLS SFG components\\\\n 2 photon\\\\n components\\\\n classical dichroic\\\\n pump splitter\\\\n Q1\\\\n Q2\\\\nFigure 6: Illustration of a deterministic Bell-state analyzer based on single-photon\\\\nnonlinearity, frequency conversion, and linear optics. Upper plot: a deterministic two-\\\\nlevel scattering (TLS) process induced, e.g., in a nanophotonic waveguide may be\\\\noperated such that one and two photons are scattered to orthogonal spatio-temporal\\\\nmodes. The two modes can subsequently be selected by implementing active filtering,\\\\ne.g. nonlinear sum-frequency generation (SFG) where the spatio-temporal mode of the\\\\nclassical pump is matched to, e.g., the one-photon component of the scattered light.\\\\nConsequently, only the single-photon component is frequency converted and can be\\\\nseparated from the two-photon component by a dichroic splitter. Lower plot: optical\\\\ncircuit containing four photon-sorting elements that is capable of determining all four\\\\npath-encoded Bell states by photon coincidence detection. Figure reproduced from Ref.\\\\n[45].\\\\n\\\\ninteraction to deterministically swap the quantum state of the emitter and a photon\\\\n[67, 68]. A full architecture of a quantum network based on QDs chirally coupled to\\\\nnanophotonic waveguides has been put forward in Ref. [56], cf. Fig. 5. Here the very\\\\nlarge cooperativity obtainable with QDs leads to a gate fidelity approaching unity for\\\\nexperimentally realistic parameters.\\\\n Another highly exciting direction is to exploit a single QD efficiently coupled to a\\\\nnanophotonic waveguide to directly produce entangled photons. To this end, Lindner\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'a5cf3e02-ee6b-47ea-9b4d-e330002f40f5\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 14\\\\n\\\\nand Rudolph have put forward the proposal of a photonic ”cluster state machine gun”\\\\nbased on a QD that could potentially producing long strings of photons in an entangled\\\\ncluster state [69]. In this proposal, a single QD spin is brought into a superposition\\\\nstate of up and down and optically excited to a superposition of charged exciton states.\\\\nSubsequently the QD decays by spontaneous emission of a single photon thereby creating\\\\nan entangled state between the polarization of the emitted photon and the spin state.\\\\nThe excitation-emission process can be repeated multiple times in which case a large\\\\nGHZ photonic state is generated. If π/2 rotations on the QD spin are carried out\\\\nin-between photon emissions, an entangled photonic cluster state is created. A QD\\\\nembedded in a nanophotonic waveguide could potentially serve as a source of large 1D\\\\ncluster states, where the cluster size is determined by the number of emission events\\\\n(determined by the QD lifetime that is typically ∼ 100 ps in a nanophotonic structure)\\\\nthat can take place within the coherence time of the spin (up to ∼ μs). A pioneering\\\\nexperiment of photonic cluster state generation with a QD source has recently been\\\\nreported [9], which validates the approach.\\\\n Cluster states have attracted a lot of attention since it was realized that they enable\\\\nuniversal quantum computing. The general philosophy behind such one-way quantum\\\\ncomputing architectures [70] is that a large scale entangled state is generated ”up front”\\\\nand computation carried out by single-qubit operations induced by measurements.\\\\nImportantly, quantum computing requires access to 2D photonic cluster states as\\\\nopposed to the 1D cluster states considered above. Several proposals of how to\\\\nachieve this with QDs have been put forward. One approach is to optically couple\\\\ntwo QDs [71], which requires local tuning of the two QDs into mutual resonance. An\\\\nalternative proposal has been put forward based on a single quantum emitter, where the\\\\nemitted photons are controllably coupled back to interact with the emitter and through\\\\nthis process generates the 2D cluster [72]. Such a ”one-emitter quantum-computer\\\\narchitecture” is almost ideally suited for implementation based on QDs in nanophotonic\\\\nstructures. Another exciting potential applications of photonic cluster states would be in\\\\nquantum repeater protocols without the necessity of a matter-based quantum memory\\\\n[73, 74]. Such an approach would potentially overcome the main bottleneck of the QD\\\\nbased platform for quantum networks resulting from the relatively short-lived quantum\\\\nmemory offered by the QD spin.\\\\n\\\\n8. Conclusions\\\\n\\\\nQDs in photonic nanostructures constitute an unprecedent photon-emitter interface at\\\\noptical frequencies. The recent significant progress implies that advanced quantum-\\\\ninformation processing protocols now become within experimental reach in the overall\\\\nquest towards constructing photonic quantum networks. Such a distributed photonic\\\\nquantum network would open new avenues for secure quantum communication and\\\\nultimately provide a way of scaling up quantum computers. It is currently an exciting\\\\nand timely research topic to identify how the emerging quantum hardware can most\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'2ba64cf8-ce27-4486-8bc8-6dc319c814d7\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'Quantum-dot based photonic quantum networks 15\\\\n\\\\nefficiently be ’put to use’ by laying out realistic and resource efficient quantum network\\\\narchitectures. Such a research programme requests a close interplay between theory and\\\\nexperiment in a continuous effort on how to exploit new quantum technology.\\\\n\\\\n9. Acknowledgements\\\\n\\\\nIt is a pleasure to acknowledge all the excellent colleagues who have contributed to my\\\\nunderstanding of solid-state photonic quantum networks including A.S. Sørensen, R.J.\\\\nWarburton, T.C. Ralph, and A.G. White, as well as all past and present members of\\\\nthe Quantum Photonics Group at the Niels Bohr Institute, University of Copenhagen.\\\\nI thank Nir Rotenberg for proof reading the manuscript. I gratefully acknowledge\\\\nfinancial support from the European Research Council (ERC Advanced Grant SCALE ),\\\\nInnovation Fund Denmark (Quantum Innovation Center Qubiz ), and the Danish Council\\\\nfor Independent Research.\\\\n [1] H. J. Kimble, The quantum internet, Nature 453, 1023 (2008).\\\\n [2] A. Reiserer and G. Rempe, Cavity-based quantum networks with single atoms and optical photons,\\\\n Rev. Mod. Phys. 87, 1379 (2015).\\\\n [3] A.V. Kuhlmann, J. Houel, A. Ludwig, L. Greuter, D. Reuter, A.D. Wieck, M. Poggio, and R.J.\\\\n Warburton, Charge noise and spin noise in a semiconductor quantum device, Nature Phys. 9,\\\\n 570 (2013).\\\\n [4] N. Somaschi, V. Giesz, L. De Santis, J. C. Loredo, M. P. Almeida, G. Hornecker, S. L. Portalupi,\\\\n T. Grange, C. Anton, J. Demory, C. Gomez, I. Sagnes, N. D. Lanzillotti-Kimura, A. Lema ˜Atre,\\\\n A. Auffeves, A. G. White, L. Lanco, and P. Senellart, Near-optimal single-photon sources in the\\\\n solid state, Nature Phot. 10, 340 (2016).\\\\n [5] X. Ding, Y. He, Z.-C. Duan, N. Gregersen, M.-C. Chen, S. Unsleber, S. Maier, C. Schneider, M.\\\\n Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, On-demand single photons with high extraction\\\\n efficiency and near-unity indistinguishability from a resonantly driven quantum dot in a\\\\n micropillar, Phys. Rev. Lett. 116, 020401 (2016).\\\\n [6] A.V. Kuhlmann, J.H. Prechtel, J. Houel, A. Ludwig, D. Reuter, A.D. Wieck, and R.J. Warburton,\\\\n Transform-limited single photons from a single quantum dot, Nature Comm. 6, 8204 (2015)\\\\n [7] P. Lodahl, S. Mahmoodian, and S. Stobbe, Interfacing single photons and single quantum dots\\\\n with photonic nanostructures, Rev. Mod. Phys. 87, 347 (2015).\\\\n [8] M. Arcari, I. S¨ollner, A. Javadi, S. Lindskov Hansen, S. Mahmoodian, J. Liu, H. Thyrrestrup,\\\\n E. H. Lee, J. D. Song, S. Stobbe, and P. Lodahl, Near-unity coupling efficiency of a quantum\\\\n emitter to a photonic crystal waveguide, Phys. Rev. Lett. 113, 093603 (2014).\\\\n [9] I. Schwartz, D. Cogan, E.R. Schmidgall, Y. Don, L. Gantz, O. Kenneth, N.H. Lindner, and D.\\\\n Gershoni, Deterministic generation of a cluster state of entangled photons, Science 354, 434\\\\n (2016).\\\\n[10] H. Wang, Y. He, Y.-H. Li, Z.-E. Su, B. Li, H.-L. Huang, X. Ding, M.-C. Chen, C. Liu, J. Qin,\\\\n J.-P. Li, Y.-M. He, C. Schneider, M. Kamp, C.-Z. Peng, S. Hoefling, C.-Y. Lu, and J.-W. Pan,\\\\n High-efficiency multiphoton boson sampling, Nature Phot. 11, 361 (2017).\\\\n[11] C.H.H. Schulte, J. Hansom, A.E. Jones, C. Matthiesen, C. Le Gall, and M. Atature, Quadrature\\\\n squeezed photons from a two-level system, Nature 525, 222 (2015).\\\\n[12] D. Huber, M. Reindl, Y. Huo, H. Huang, J.S. Wildmann, O.G. Schmidt, A. Rastelli, and R.\\\\n Trotta, Highly indistinguishable and strongly entangled photons from symmetric GaAs quantum\\\\n dots, Nature Comm. 8, 15506 (2017)\\\\n[13] K. De Greve, P.L. McMahon, D. Press, T.D. Ladd, D. Bisping, C. Schneider, M. Kamp, L.\\\\n Worschech, S. Hofling, A. Forchel, and Y. Yamamoto, Ultrafast coherent control and suppressed\\\\n nuclear feedback of a single quantum dot hole qubit, Nature Physics 7, 872 (2011).\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'9e6eac23-ac2f-4095-83e3-51fbd271c644\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' Quantum-dot based photonic quantum networks 16\\\\n\\\\n [14] J.H. Prechtel, A.V. Kuhlmann, J. Houel, A. Ludwig, S.R. Valentin, A.D. Wieck, and R.J.\\\\n Warburton, Decoupling a hole spin qubit from the nuclear spins, Nature Materials 15, 981\\\\n (2016)\\\\n[15] K. Hammerer, A.S. Sørensen, and E.S. Polzik, Quantum interface between light and atomic\\\\n ensembles, Rev. Mod. Phys. 82, 1041 (2010).\\\\n [16] C. Santori, D. Fattal, J. Vukovic, G.S. Solomon, and Y. Yamamoto, Indistinguishable photons from\\\\n a single-photon device, Nature 419, 594 (2002).\\\\n[17] P. Michler, A. Kiraz, C. Becher, W.V. Schoenfeld, P.M. Petroff, L. Zhang, E. Hu, and A. Imamoglu,\\\\n A quantum dot single-photon turnstile device, Science 290, 2282 (2000).\\\\n[18] Y.-M. He, Y. He, Y.-J. Wei, D. Wu, M. Atature, C. Schneider, S. Hofling, M. Kamp, C.-Y. Lu, and\\\\n J.-W. Pan, On-demand semiconductor single-photon source with near-unity indistinguishability,\\\\n Nature Nano. 8, 213 (2013).\\\\n[19] R.J. Warburton, Single spins in self-assembled quantum dots, Nature Materials 12, 483 (2013).\\\\n [20] J. Bleuse, J. Claudon, M. Creasey, N.S. Malik, J.-M. Gerard, I. Maksymov, J.-P. Hugonin, and\\\\n P. Lalanne Inhibition, enhancement, and control of spontaneous emission in photonic nanowires\\\\n Phys. Rev. Lett. 106, 103601 (2011).\\\\n [21] M. Davanco, M.T. Rakher, W. Wegscheider, D. Schuh, A. Badolato, and K. Srinivasan, Efficient\\\\n quantum dot single photon extraction into an optical fiber using a nanophotonic directional\\\\n coupler, Appl. Phys. Lett. 99, 121101 (2011).\\\\n [22] T.G. Tiecke, K.P. Nayak, J.D. Thompson, T. Peyronel, N.P. de Leon, V. Vuletic, and M.D. Lukin,\\\\n Efficient fiber-optical interface for nanophotonic devices, Optica 2, 70 (2015).\\\\n [23] R.S. Daveau, K.C. Balram, T. Pregnolato, J. Liu, E.H. Lee, J.D. Song, V. Verma, R. Mirin, S. Woo\\\\n Nam, L. Midolo, S. Stobbe, K. Srinivasan, and P. Lodahl, Efficient fiber-coupled single-photon\\\\n source based on quantum dots in a photonic-crystal waveguide, Optica 4, 178 (2017).\\\\n [24] A. Kiraz, M. Atature, and A. Imamoglu, Quantum-dot single-photon sources: Prospects for\\\\n applications in linear optics quantum-information processing, Phys. Rev. A 69, 032305 (2004).\\\\n [25] P. Tighineanu, C.L. Dreessen, C. Flindt, P. Lodahl, and A.S. Sørensen, Phonon decoherence\\\\n of quantum dots in photonic structures: Broadening of the zero-phonon line and the role of\\\\n dimensionality, arXiv:1702.04812 (2017).\\\\n [26] G. Kirsanske, H. Thyrrestrup, R.S. Daveau, C.L. Dreessen, T. Pregnolato, L. Midolo, P.\\\\n Tighineanu, A. Javadi, S. Stobbe, R. Schott, A. Ludwig, A.D. Wieck, S.I Park, J.D. Song,\\\\n A.V. Kuhlmann, I. Sollner, M.C. Lobl, R.J. Warburton, and P. Lodahl, Indistinguishable and\\\\n efficient single photons from a quantum dot in a planar nanobeam waveguide, arXiv:1701.08131\\\\n (2017).\\\\n [27] H. Wang, Z.-C. Duan, Y.-H. Li, S. Chen, J.-P. Li, Y.-M. He, M.-C. Chen, Y. He, X. Ding, C.-Z.\\\\n Peng, C. Schneider, M. Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, Near-transform-limited\\\\n single photons from an efficient solid-state quantum emitter, Phys. Rev. Lett. 116, 213601\\\\n (2016).\\\\n [28] J.L. O’Brien, A. Furusawa, and J. Vuckovic, Photonic quantum technologies, Nature Photonics 3,\\\\n 687 (2009).\\\\n [29] J. Carolan, C. Harrold, C. Sparrow, E. Martin-Lopez, N.J. Russell, J.W. Silverstone, P.J. Shadbolt,\\\\n N. Matsuda, M. Oguma, M. Itoh, G.D. Marshall, M.G. Thompson, J.C.F. Matthews, T.\\\\n Hashimoto, J.L. O’Brien, and A. Laing, Universal linear optics, Science 349, 711 (2015).\\\\n [30] J. Wang, A. Santamato, P. Jiang, D. Bonneau, E. Engin, J.W. Silverstone, M. Lermer, J. Beetz, M.\\\\n Kamp, S. Hofling, M.G. Tanner, C.M. Natarajan, R.H. Hadfield, S.N. Dorenbos, V. Zwiller, J.L.\\\\n O’Brien, and M.G. Thompson, Gallium arsenide (GaAs) quantum photonic waveguide circuits,\\\\n Opt. Commun. 327, 49 (2014).\\\\n [31] L. Midolo et al., in preparation (2017).\\\\n [32] L. Midolo, A. Schliesser, and A. Fiore, in preparation (2017).\\\\n [33] A. Aspuru-Guzik and P. Walther, Photonic quantum simulators Nature Physics 8, 285 (2012).\\\\n [34] J. Kettler, M. Paul, F. Olbrich, K. Zeuner, M. Jetter, and P. Michler, Single-photon and photon\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'4a9600a2-24a1-4600-9fb2-d10ba3b7c33a\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' Quantum-dot based photonic quantum networks 17\\\\n\\\\n pair emission from MOVPE-grown In(Ga)As quantum dots: shifting the emission wavelength\\\\n from 1.0 to 1.3 μm Appl. Phys. B 122, 48 (2016).\\\\n [35] J.-H. Kim, T. Cai, C.J.K. Richardson, R.P. Leavitt, and E. Waks, Two-photon interference from\\\\n a bright single-photon source at telecom wavelengths, Optica 3, 577 (2016).\\\\n [36] B. Kambs, J. Kettler, M. Bock, J.N. Becker, C. Arend, A. Lenhard, S.L. Portalupi, M. Jetter,\\\\n P. Michler, and C. Becher, Low-noise quantum frequency down-conversion of indistinguishable\\\\n photons, Opt. Express 24, 22250 (2016).\\\\n [37] R.H. Hadfield, Single-photon detectors for optical quantum information applications, Nature\\\\n Photonics 3, 696 (2009).\\\\n[38] G. Reithmaier, S. Lichtmannecker, T. Reichert, P. Hasch, K. Muller, M. Bichler, R. Gross, and J.J.\\\\n Finley, On-chip time resolved detection of quantum dot emission using integrated superconducting\\\\n single photon detectors, Sci. Reports 3, 1901 (2013).\\\\n[39] A. Faraon, I. Fushman, D. Englund, N. Stoltz, P. Petroff, and J. Vukovic, Coherent generation of\\\\n non-classical light on a chip via photon-induced tunnelling and blockade, Nature Phys. 4, 859\\\\n (2008).\\\\n[40] A. Reinhard, T. Volz, M. Winger, A. Badolato, K.J. Hennessy, E.L. Hu, and A. Imamoglu, Strongly\\\\n correlated photons on a chip, Nature Phot. 6, 93 (2012).\\\\n[41] M.P. Bakker, T. Ruytenberg, W. Loffler, A. Barve, L. Coldren, M.P. van Exter, and D.\\\\n Bouwmeester, Quantum dot nonlinearity through cavity-enhanced feedback with a charge memory\\\\n Phys. Rev. B 91, 241305(R) (2015)\\\\n[42] A.J. Bennett, J.P. Lee, D.J.P. Ellis, I. Farrer, D.A. Ritchie, and A.J. Shields, A semiconductor\\\\n photon-sorter Nature Nano. 11, 857 (2016).\\\\n[43] A. Javadi, I. S¨ollner, M. Arcari, S.L. Hansen, L. Midolo, S. Mahmoodian, G. Kirsanske, T.\\\\n Pregnolato, E.H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Single-photon non-linear optics\\\\n with a quantum dot in a waveguide, Nature Comm. 6, 8655 (2015)\\\\n[44] D. Witthaut, M.D. Lukin, and A.S. Sørensen, Photon sorters and QND detectors using single\\\\n photon emitters, Europhys. Lett. 97, 50007 (2012).\\\\n[45] T.C. Ralph, I. S¨ollner, S. Mahmoodian, A.G. White, and P. Lodahl, Photon sorting, efficient Bell\\\\n measurements and deterministic CZ gate using a passive two-level nonlinearity, Phys. Rev.\\\\n Lett. 114, 173603 (2015).\\\\n[46] D.E. Chang, A.S. Sørensen, E.A. Demler, and M.D. Lukin, A single-photon transistor using\\\\n nanoscale surface plasmons, Nature Phys. 3, 807 (2007).\\\\n [47] B. Urbaszek, X. Marie, T. Amand, O. Krebs, P. Voisin, P. Maletinsky, A. Hogele, and A. Imamoglu,\\\\n Nuclear spin physics in quantum dots: An optical investigation, Rev. Mod. Phys. 85, 79 (2013).\\\\n [48] K. Heshami, C. Santori, B. Khanaliloo, C. Healey, V.M. Acosta, P.E. Barclay, and C. Simon,\\\\n Raman quantum memory based on an ensemble of nitrogen-vacancy centers coupled to a\\\\n microcavity, Phys. Rev. A 89, 040301(R) (2014).\\\\n [49] H.M. Meyer, R. Stockill, M. Steiner, C. Le Gall, C. Matthiesen, E. Clarke, A. Ludwig, J. Reichel,\\\\n M. Atature, and M. Kohl, Direct photonic coupling of a semiconductor quantum dot and a\\\\n trapped ion, Phys. Rev. Lett. 114, 123001 (2015).\\\\n [50] W.B. Gao, P. Fallahi, E. Togan, J. Miguel-Sanchez, and A. Imamoglu, Observation of entanglement\\\\n between a quantum dot spin and a single photon, Nature 491, 426 (2012).\\\\n[51] W.B. Gao, P. Fallahi, E. Togan, A. Delteil, Y.S. Chin, J. Miguel-Sanchez, and A. Imamoglu,\\\\n Quantum teleportation from a propagating photon to a solid-state spin qubit, Nature Comm. 4,\\\\n 2744 (2013).\\\\n[52] A. Delteil, Z. Sun, W.B. Gao, E. Togan, S. Faelt, and A. Imamoglu, Generation of heralded\\\\n entanglement between distant hole spins, Nature Phys. 12, 218 (2016).\\\\n[53] P. Lodahl, S. Mahmoodian, S. Stobbe, A. Rauschenbeutel, P. Schneeweiss, J. Volz, H. Pichler, and\\\\n P. Zoller, Chiral quantum optics, Nature 541, 473 (2017).\\\\n[54] I. S¨ollner, S. Mahmoodian, S. Lindskov Hansen, L. Midolo, A. Javadi, G. Kirsanske, T. Pregnolato,\\\\n H. El-Ella, E.-H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Deterministic photon-emitter\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'9d6d7ce1-ecb5-4330-ab7f-ae5c4747b61c\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum-dot based photonic quantum networks\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' Quantum-dot based photonic quantum networks 18\\\\n coupling in chiral photonic circuits, Nature Nano. 10, 775 A (2015).ˆ\\\\n [55] R.J. Coles, D.M. Price, J.E. Dixon, B. Royall, E. Clarke, P. Kok, M.S. Skolnick, A.M. Fox,\\\\n and M.N. Makhonin, Chirality of nanophotonic waveguide with embedded quantum emitter for\\\\n unidirectional spin transfer, Nature Comm. 7, 11183 (2016).\\\\n [56] S. Mahmoodian, ˆA P. Lodahl, and ˆA A. S. Sørensen, Quantum networks with chiral light-matter\\\\n interaction in waveguides, Phys. Rev. Lett. 117, 240501 (2016).\\\\n [57] P.W. Fry, I.E. Itskevich, D.J. Mowbray, M.S. Skolnick, J.J. Finley, J.A. Barker, E.P. O’Reilly, L.R.\\\\n Wilson, I.A. Larkin, P.A. Maksym, M. Hopkinson, M. Al-Khafaji, J.P.R. David, A.G. Cullis,\\\\n G. Hill, and J.C. Clark, Inverted electron-hole alignment in InAs-GaAs self-assembled quantum\\\\n dots, Phys. Rev. Lett. 84, 733 (2000).\\\\n [58] F. Ding, R. Singh, J.D. Plumhof, T. Zander, V. Krapek, Y.H. Chen, M. Benyoucef, V. Zwiller, K.\\\\n Dorr, G. Bester, A. Rastelli, and O.G. Schmidt, Tuning the exciton binding energies in single\\\\n self-assembled InGaAs/GaAs quantum dots by piezoelectric-induced biaxial stress, Phys. Rev.\\\\n Lett. 104, 067405 (2010).\\\\n [59] R. Stockill, M.J. Stanley, L. Huthmacher, E. Clarke, M. Hugues, A.J. Miller, C. Matthiesen, C.\\\\n Le Gall, and M. Atature, Phase-tuned entangled state generation between distant spin qubits,\\\\n arXiv:1702.03422 (2017).\\\\n [60] D. Dzsotjan, A.S. Sørensen, and M. Fleischhauer, Quantum emitters coupled to surface plasmons\\\\n of a nanowire: A Greens function approach, Phys. Rev. B 82, 075427 (2010).\\\\n [61] T. Ramos, H. Pichler, A.J. Daley, and P. Zoller, Quantum spin dimers from chiral dissipation in\\\\n cold-atom chains, Phys. Rev. Lett. 113, 237203 (2014).\\\\n[62] K.M. Weiss, J.M. Elzerman, Y.L. Delley, J. Miguel-Sanchez, and A. Imamoglu, Coherent two-\\\\n electron spin qubits in an optically active pair of coupled InGaAs quantum dots, Phys. Rev.\\\\n Lett. 109, 107401 (2012).\\\\n[63] T. Rudolph, Why I am optimistic about the silicon-photonic route to quantum computing, APL\\\\n Photonics 2, 030901 (2017).\\\\n[64] J.H. Shapiro, Single-photon Kerr nonlinearities do not help quantum computation, Phys. Rev. A\\\\n 73, 062305 (2006).\\\\n[65] D. Gottesman and I.L. Chuang, Demonstrating the viability of universal quantum computation\\\\n using teleportation and single-qubit operations, Nature 402, 390 (1999).\\\\n[66] L.M. Duan and H.J. Kimble, Scalable photonic quantum computation through cavity-assisted\\\\n[67] K. Koshino, S. Ishizaka, and Y. Nakamura, Deterministic photon-photon √SWAP gate using a Λinteractions, Phys. Rev. Lett. 92, 127902 (2004).\\\\n system, Phys. Rev. A 82, 010301(R) (2010).\\\\n[68] S. Rosenblum, A. Borne, and B. Dayan, Analysis of deterministic swapping of photonic and atomic\\\\n states through single-photon Raman interaction, Phys. Rev. A 95, 033814 (2017).\\\\n[69] N.H. Lindner and T. Rudolph, Proposal for pulsed on-demand sources of photonic cluster state\\\\n strings, Phys. Rev. Lett. 103, 113602 (2009).\\\\n[70] R. Raussendorf, D.E. Browne, and H.J. Briegel, Measurement-based quantum computation on\\\\n cluster states, Phys. Rev. A 68, 022312 (2003).\\\\n[71] S.E. Economou, N. Lindner, and T. Rudolph, Optically generated 2-dimensional photonic cluster\\\\n state from coupled quantum dots, Phys. Rev. Lett. 105, 093601 (2010).\\\\n[72] H. Pichler, S. Choi, P. Zoller, and M.D. Lukin, Photonic tensor networks produced by a single\\\\n quantum emitter, arXiv:1702.02119\\\\n [73] K. T. K. Azuma and H.-K. Lo, All-photonic quantum repeaters, Nat. Commun. 6, 6787 (2015).\\\\n [74] D. Buterakos, E. Barnes, and S.E. Economou, Deterministic generation of all-photonic quantum\\\\n repeaters from solid-state emitters, arXiv:1612.03869 (2016).\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}]}, {\\'documents\\': [{\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'be0e8bc0-0012-40bf-90b8-0e50f67a0433\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'arXiv:quant-ph/0407030v1 5 Jul 2004\\\\n\\\\n On quantum state of entangled photon pairs\\\\n Ruo Peng WANG\\\\n Department of Physics, Peking University, Beijing 100871, P.R.China\\\\n Email: rpwang@cis.pku.edu.cn\\\\n June 18, 2018\\\\n\\\\n Abstract\\\\n I show that the photon pairs used in experimental tests of quantum\\\\n non-locality based on Bell’s theorem are not in the entangled quantum\\\\n state. The correct quantum state of the “entangled” photon pairs is sug-\\\\n gested. Two experiments for testing this quantum state are proposed.\\\\n PACS: 03.65.Ud, 42.50.Dv\\\\n Keywords: entangled photon pair, entangled quantum states, quantum\\\\n non-locality\\\\n\\\\n 1 introduction\\\\n Quantum non-locality is a controversial topic of quantum theory, and entangled\\\\n photon pairs played a very important role in experimental tests of quantum non-\\\\n locality. So far, a number of experimental tests of quantum non-locality based\\\\n on Bell’s theorem [1] have been carried out [2, 3, 4, 5, 6]. In these experiments\\\\n entangled photon pairs are produced, and the polarization correlation between\\\\n entangled photon pairs is measured. Experimental results obtained in these tests\\\\n are in favor for quantum non-locality. Although it has been pointed out that\\\\n this polarization correlation is compatible with local realism [7], these results\\\\n are generally considered as direct evidences for the existence of quantum non-\\\\n locality, and more experiments [8, 9, 10] showing different kind of quantum\\\\n non-local correlation are carried out. The entangled photon pairs are used as\\\\n light source in these experiments, and the interpretation of the experimental\\\\n results as proofs for quantum non-locality is closely related to the assumption\\\\n that these photon pairs are in entangled quantum state.\\\\n In this letter, I will show that the same polarization correlation also exists\\\\n for certain un-entangled photon pairs. And on other hand, based on the consid-\\\\n eration of momentum conservation, I will also show that the entangled quantum\\\\n state is not a correct description for photon pairs used in these experimental\\\\n tests of quantum non-locality. A correct quantum state for these photon pairs is\\\\n suggested, and two experiments for testing this quantum state and the quantum\\\\n non-locality are proposed.\\\\n\\\\n 1\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'0e51993f-ad65-4849-aaa5-3718b2edf1c8\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'2 polarization correlation between un-entangled\\\\n photons\\\\nLet’s consider a beam of un-entangled photon pairs with each photon pair con-\\\\ntaining a left circle polarized photon and a right circle polarized photon. Such\\\\na beam of photon pairs can be produced by adjusting polarization state of\\\\nthe photon pairs obtained from the parametric down conversion with a type-II\\\\ncollinear phase matching [11]. The quantum state of this un-entangled photon\\\\npairs can be expressed as\\\\n |ψ〉 = 2 1(bv †+ ibh†)(bv †− ibh†)|0〉 = 2 1(bv †bv †+ bh†bh†)|0〉, (1)\\\\nwhere bv† and bh are the creation operator for vertically polarized photon and,†\\\\nrespectively, for horizontally polarized photon with the wave vector~2. By using k\\\\nthe Coulomb gauge, we may describe the optical field of these photon pairs by\\\\nthe vector potential A~. The positive frequencies part of the vector potential is\\\\nrelated to the photon annihilation operators in the following way\\\\n A+ = B(bv~v + bh~h)ei~2 ·~\\\\n ~ e e k r (2)\\\\nwhere the constant B is given by [12]\\\\n B =√ μ0ℏc2 (3)\\\\n 2V ω\\\\nwith μ0 the magnetic permeability of the vacuum, ω the angular frequency of\\\\nthe photons.\\\\n We divide the beam of un-entangled photon pairs into two channels by using\\\\na half reflecting mirror (BS). The wave vectors of photons in these two channels\\\\nare~1 and k k ~2. A half wave plate (W P1) is placed into the channel 1 to introduce\\\\na phase difference of π between the vertical and horizontal components of the\\\\noptical field, and another half wave plate (W P2) is placed into the channel 2\\\\nto swap the vertical and horizontal components of the optical field, as shown\\\\nin FIG. 1. As no incident photons with the wave vector~1 being present, after k\\\\npassing the beam splitter and the wave plates, the optical field becomes [13]\\\\n A~ + = √2 (bv~v − bh~h)ei~1 ·~ + √2 (bh~v + bv~h)ei~2 ·~r .Be e k r B e e k (4)\\\\nThe photons in the channel 1 and 2 are polarized by Glan-Thompsom linear\\\\npolarization analyzers P1 and P2, respectively, before being detected by single\\\\nphoton detectors D1 and D2.\\\\n To analyze the polarization correlation between photons detected by detec-\\\\ntors D1 and D2, we need expressions of optical field at these detectors. Up to\\\\na phase factor, we have\\\\n A+~θ1 = √2 (bv cos θ1 − bh sin θ1)~θ1B e (5)\\\\n 2\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'99eaa171-c4c6-43bb-8b9c-6deeffc5c6fc\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' 1 D1\\\\n 2\\\\n 1(b bh\\\\n 2 h † †+b bvv † †) 0 1 2 WP1 θ1\\\\n 1 P1\\\\n 1 θ2\\\\n 2 BS 2\\\\n 1 WP2\\\\n 2 2 P2\\\\n 1 D2\\\\n\\\\nFigure 1: A schematic illustration of the experimental setup for testing the\\\\npolarization correlation between un-entangled photon pairs. The polarization\\\\nstates of optical beams are indicated by arrows.\\\\n\\\\nand B\\\\n A+~θ2 = √2 (bh cos θ2 + bv sin θ2)~θ2 .e (6)\\\\nIn the above expressions, ~θ1 and ~θ2 are vectors of unit of the transmissione e\\\\naxes of the analyzer P1, and respectively, P2. The relations (5) and (6) can be\\\\nrewritten in the following form\\\\n A+~θ1 = √2 bθ1 ~θ1 , AθB e ~+2 = √2 bθ2~θ2B e (7)\\\\nwhere\\\\n bθ1 = bv cos θ1 − bh sin θ1, bθ2 = bh cos θ2 + bv sin θ2 (8)\\\\nare the annihilation operators for the photon polarized in the direction ~θ1 withe\\\\nthe wave vector~1 and, respectively, for the photon polarized in the direction k\\\\n~θ2 with the wave vector~2.\\\\ne k\\\\n The coincidence counting rate C(θ1, θ2) is proportional to the probability\\\\nof annihilating simultaneously one photon polarized in the direction ~θ1 at thee\\\\ndetector D1 and one photon polarized in the direction ~θ2 at the detector D2.e\\\\nThis probability is, in its turn, proportional the matrix element\\\\n 〈ψ|bθ2 b†1 bθ1bθ2|ψ〉 = 〈ψ|(bv † †cos θ1 − bh sin θ1)(bh cos θ2 + bvsin θ2)† † †\\\\n θ\\\\n × (bv cos θ1 − bh sin θ1)(bh cos θ2 + bv sin θ2)|ψ〉 (9)\\\\n = 4 1sin2(θ1 − θ2).\\\\nTherefore we have C(θ1, θ2) = CM sin2(θ1 − θ2), (10)\\\\n\\\\n 3\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'f98c627e-47a9-42c3-83bb-6111f9b06525\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'where CM this the maxim counting rate that occurs at θ1 − θ2 = ±π/2. This\\\\ncorrelation is just the same as in the case of entangled photon pairs [6, 11], and\\\\nthe Bell’s inequality is apparently violated also in the above discussed experi-\\\\nment. The violation of the Bell’s inequality in this case is only apparent, because\\\\na condition for obtaining the Bell’s inequality is not fulfilled here. Namely, in\\\\nthe experiment described in this paper, both photons of a photon pair could\\\\nbe detected in one single channel, with the wave vector k~1 or k~2. Thus, even\\\\nthough the same polarization correlation as in the case of entangled photon\\\\npairs is realized in this experiment, no quantum non-locality is involved here.\\\\nIndeed, after annihilating of a linear polarized photon at the detector D1, the\\\\nquantum state becomes\\\\n |ψ′〉 = bθ1 |ψ〉 = 1√2 (cos θ1b† − sin θ1b†)|0〉.v h (11)\\\\nThe photon in the quantum state |ψ′〉 is not spatially separated from the detector\\\\nD1 where another photon was annihilated. The probability as the photon in\\\\nthe quantum state |ψ′〉 being detected at D1 is\\\\n 〈ψ′|bθ1 bθ1 |ψ′〉 = 2 † 1. (12)\\\\n3 quantum state of entangled photon pairs\\\\nNow we arrive at a very important point: the observation of a photon polar-\\\\nization correlation as expressed in the relation (10) does not form a sufficient\\\\nevidence for quantum non-locality. Besides this correlation, one must exclude\\\\nthe possibility of detecting both of the photons in one single channel to ensure\\\\nthat the phenomena of quantum non-locality are really observed.\\\\n An apparently non-local correlation as expressed in the relation (10) are ob-\\\\nserved experimentally for the entangled photon pairs generated in the paramet-\\\\nric down conversion nonlinear optical processes with a type-II phase matching.\\\\nWe have to see if the possibility of detecting both of the photons in one single\\\\nchannel can be ruled out in the experiment so that one can really interpret the\\\\nexperimental results as an experimental evidence for quantum non-locality. So\\\\nfar the following quantum state is used to describe this photon pair[11]:\\\\n |ψe〉 = 1√2 (bv†1b†2 − bvh †2b†1)|0〉,h (13)\\\\nwhere bh1, b†1, b†2 and bv2 are creation operators for horizontally polarized pho-† †\\\\n v h\\\\ntons with the wave vector k~1, for vertically polarized photons with the wave\\\\nvector~1, for horizontally polarized photons with the wave vector k k ~2, and for\\\\nvertically polarized photons with the wave vector~2, respectively. By using the k\\\\ncorresponding photon annihilation operators, we may write the optical field as\\\\n A+ = B(bv1~v + bh1~h)ei~1 ·~ + B(bv2~v + bh2~h)ei~2 ·~.\\\\n ~e e e k r e e k r (14)\\\\n\\\\n 4\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'288c7d8f-ef47-4768-b601-8119e00d4d55\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'It is easy to verify that there exists a correlation as described by the relation\\\\n(10) between the polarization of the photon pairs in the quantum state |ψe〉.\\\\n The expression (13) for the quantum state of the entangled photon pairs was\\\\nderived by taking into consideration of the energy and the momentum conserva-\\\\ntion in the parametric down conversion process. The energy and the momentum\\\\nof a photon pair in the state |ψe〉 is equal to the energy and the momentum of\\\\nthe incident photon from which the photon pair is generated. But there is\\\\na problem in this argument, namely the parametric down conversion process\\\\ntakes place in nonlinear optical crystals, so one must take the photons and the\\\\ncrystal as one single system when the condition of the energy and the momen-\\\\ntum conservation is applied. The macroscopic properties of nonlinear optical\\\\ncrystals do not change in the parametric down conversion process. Thus the\\\\nexpectation values of the energy and the momentum of crystals are the same\\\\nbefore and after the photon pairs’ generation. Therefore the expectation val-\\\\nues of energy and momentum of a photon pair must equal to the expectation\\\\nvalues of energy and momentum of incident photon. This condition is satisfied\\\\nby the quantum state |ψe〉. On other hand, photons are coupled with optical\\\\nphonons with same momentum when passing through crystals, and there exist\\\\nexchanges of energy and momentum between photons and crystals through the\\\\nphoton-phonon interaction during photons’ propagation in crystals. One may\\\\nneglect the energy exchange between photons and crystals because the ener-\\\\ngies of phonons are much smaller than that of photons with same momentum,\\\\nbut the momentum exchange between photons and crystals must be taken into\\\\nconsideration. This exchange of momentum causes fluctuations in momenta of\\\\ncrystals and generated photon pairs. But the state |ψe〉 is an eigen-state of the\\\\nphoton pairs’ momentum, that means there is not momentum fluctuations for\\\\nphoton pairs in the state |ψe〉. Therefore the generated photon pair can not be\\\\nin this quantum state.\\\\n According to the calculation made before, the correlation between the po-\\\\nlarization of the photon pairs as described by the relation (10) does exist also\\\\nfor photon pairs in the following quantum state\\\\n |ψu〉 = 2 1(b1 †+ ib2†)(b1 †− ib2†)|0〉 = 2 1(b1†b1 †+ b2†b2†)|0〉, (15)\\\\nwith the optical field given by\\\\n A~u += √2 (b1~v − b2~h)ei~1 ·~ + √2 (b2~v + b1~h)ei~2 ·~r .B e e k r B e e k (16)\\\\nThe quantum state |ψu〉 is an un-entangled state. In fact, we can write |ψu〉 as\\\\n |ψu〉 = |ψa〉 ⊗ |ψb〉, (17)\\\\nwith †|0〉, |ψb〉 = bb†|0〉,\\\\n |ψa〉 = ba (18)\\\\nwhere †= 1 †= 1\\\\n ba √2 (b1 †+ ib2†), bb √2 (b1 †− ib2†), [ba†, bb] = 0. (19)\\\\n 5\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'43b30d97-ad73-4167-b142-fec2d80f5ce9\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' The quantum state |ψu〉 is not an eigen-state of momentum, but photon\\\\npairs in the quantum states |ψe〉 and |ψu〉 have the same expectation values of\\\\nthe energy and the momentum. So the quantum state |ψu〉 satisfies the energy\\\\nand momentum conservation requirement. In conclusion, the quantum state\\\\n|ψu〉 could be a correct description for photon pairs generated in the paramet-\\\\nric down conversion nonlinear optical processes with a type-II phase matching.\\\\nOne may observe that for photon pairs in the quantum state |ψu〉, both photons\\\\ncould be detected in one single channel. Therefore, by applying the conditions\\\\nof the energy and momentum conservation, we can not rule out the possibility\\\\nof detecting both photons in one single channel. But, instead, we find that the\\\\nphoton pairs can not be in the entangled state. The apparently non-local cor-\\\\nrelation between the polarization of photon pairs produced in the parametric\\\\ndown conversion nonlinear optical processes, that were believed being in entan-\\\\ngled states, is not a proof for the existence of the quantum non-locality, but just\\\\na necessary evidence for the fact that photon pairs are in the quantum state\\\\n|ψu〉.\\\\n The same conclusion holds also for photon pairs emitted in a radiative atomic\\\\ncascade of calcium [2, 3]. In that process, electrons which emit two photons in a\\\\nradiative cascade are well confined within the ions of calcium. The uncertainty\\\\nin the momentum of electrons implies that the photon pairs can not be in the\\\\nentangled state which has a well defined momentum. The quantum state for\\\\nphoton pairs generated in this process could be expressed as\\\\n |ψu′〉 = 1√2 (bωhb†2h+ bω† 1 ω †1v b†2v )|0〉,ω (20)\\\\nwhere bω1h, b†1v are creation operators for horizontally, and respectively, ver-†ω\\\\ntically polarized photons of circle frequency ω1, and bω2h, b†2v are the same† ω\\\\noperators for photons of circle frequency ω2.\\\\n The optical field in the channel 1 is given by\\\\n A+= g11(bω1 v~v + bωh~h)eicω1~1 ·~ + g12(bω2v~v + bω2h~h)eicω2~n1·~r\\\\n ~1 e 1 e n r e e (21)\\\\nand\\\\n A+= g21(bω1 v~v + bωh~h)eicω1~1 ·~ + g22(bω2v~v + bω2h~h)eicω2~n·~r\\\\n ~2 e e n r e e 1 (22)\\\\n 1\\\\nin the channel 2, where ~1, ~2 are vectors of unity that indicate the propaga-n n\\\\ntion directions of the channels 1 and 2, and g11, g12, g21, g22 are coefficients that\\\\ndepend on the geometry of the experiment setup. We can now calculate the\\\\nprobability of annihilating simultaneously a photon of circle frequency ω1 po-\\\\nlarized in the direction ~θ1 in the channel 1 and a photon of circle frequency ω2e\\\\npolarized in the direction ~θ2 in the channel 2. By using the expressions (20),e\\\\n(21) and (22), we find\\\\n C12(θ1, θ2) = C′M cos2(θ1 − θ2). (23)\\\\nThis is exactly the correlation observed in experimental tests carried out by\\\\nAspect et al. [2, 3]. Again, no quantum non-locality is evolved, and both of the\\\\nphotons can be detected in one single channel also in these tests.\\\\n\\\\n 6\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'120d303e-23a4-49c2-8a47-0a738f91d646\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' D1\\\\n θ1\\\\n 1 P1\\\\n “entangled”\\\\n photon pair 2 BS θ4\\\\n 4\\\\n P3 3 P4\\\\n θ3 D4\\\\n D3\\\\n\\\\nFigure 2: A schematic illustration of the experimental setup for testing the\\\\npossibility of detecting both photons from one “entangled” photon pair in one\\\\nsingle channel. The polarization states of optical beams are indicated by arrows.\\\\n\\\\n4 proposal for experimental tests\\\\nDirect experimental tests on the possibility of detecting both photons in one\\\\nsingle channel and on the coherence of photons in different channels could make\\\\nour conclusions on the quantum state of “entangled” photons pairs and quan-\\\\ntum non-locality more convincing. An experimental test on the possibility of\\\\ndetecting both of the photons in one single channel can be done by using an\\\\nexperimental setup shown in FIG. 2. This setup is quite similar to that used\\\\nfor tests of quantum non-locality based on Bell’s theorem. But a half reflecting\\\\nmirror is inserted now into the channel 2 to split it into the channels 3 and 4,\\\\nand the coincidence counting rate between the channels 3 and 4 is measured.\\\\nThis coincidence counting rate is proportional to the polarization correlation\\\\nbetween photons in the channel 3 and in the channel 4. An anti-coincidence\\\\ncondition with the signal from the channel 1 can also be applied to ensure that\\\\nthis coincidence counting rate is not from other sources. By using the expres-\\\\nsions (15), (16), (13) and (14), one can easily verify that the following relation\\\\nholds for this coincidence counting rate\\\\n C(θ3, θ4) = CMcos2(θ3 − θ4),′′ (24)\\\\nif the photon pairs are in the quantum state |ψu〉, and\\\\n\\\\n C(θ3, θ4) ≡ 0, (25)\\\\nif the photon pairs are in the quantum state |ψe〉.\\\\n The coherence of photons in different channels can also be used for testing\\\\nthe quantum state of “entangled” photon pairs. An experimental setup for such\\\\na test is schematically illustrated in FIG. 3. The linear polarizers P1 and P2 are\\\\n\\\\n 7\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'7a329a04-32f6-4bd5-911c-1f787517abfb\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\' M1\\\\n P1\\\\n 1 WP D\\\\n “entangled”\\\\n photon pair 2\\\\n (x,y)\\\\n P2\\\\n M2\\\\n\\\\nFigure 3: A schematic illustration of the experimental setup for testing the\\\\ncoherence between photons from one “entangled” photon pair in different chan-\\\\nnels. The polarization states of optical beams are indicated by arrows.\\\\n\\\\ninserted into the beams of “entangled” photon pairs, generated in the parametric\\\\ndown conversion nonlinear optical processes with a type-II phase matching, in\\\\nsuch a way, so that the photon in the channel 1 becomes horizontally polarized,\\\\nwhile the photon in the channel 2 is polarized vertically. The polarization of\\\\nthe beam 1 is changed to vertical later by the half wave plate W P . Both beams\\\\nare reflected by the mirrors M1 and M2 to overlap each other. The single\\\\nphoton counting rate I(x, y) in the (x, y) plan as a function of the coordinates\\\\n(x, y) is measured by using the single photon detector D. Let f1(x, y)~v bee\\\\nthe distribution of the vector potential of the beam 1 in the plan (x, y), and\\\\nf2(x, y)~v the distribution of the vector potential of the beam 2 . If the photone\\\\npair were in the quantum state |ψe〉, we have\\\\n A+(x, y) = bh1f1(x, y)~v + bv2f2(x, y)~v .\\\\n ~ e e (26)\\\\n The single photon counting rate I(x, y) is proportional to the matrix element\\\\n I(x, y) ∝ 〈ψe|~+†(x, y) · A+(x, y)|ψe〉. A~ (27)\\\\nBy using the relation (26) and the expression (13) for |ψe〉, we find, for photon\\\\npairs in the quantum state |ψe〉,\\\\n I(x, y) ∝ |f1(x, y)|2 + |f2(x, y)|2, (28)\\\\nso no interference occurs. But if the photon pairs were in the quantum state\\\\n|ψu〉, then according to Eq.(16), we have\\\\n A+(x, y) = b2f1(x, y)~v + b2f2(x, y)~v .\\\\n ~ e e (29)\\\\nAnd in this case the counting rate becomes\\\\n I(x, y) ∝ |f1(x, y) + f2(x, y)|2, (30)\\\\nthat means the beam 1 and beam 2 prepared in the way described above are\\\\ncoherent.\\\\n\\\\n 8\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'8e86a476-b4c4-4c21-b244-d82f87eca2a4\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'5 discussion\\\\nI have shown that the correlations of photons’ polarization observed in “en-\\\\ntangled” photon pairs generated in the parametric down conversion nonlinear\\\\noptical processes with a type-II phase matching and in a radiative atomic cas-\\\\ncade of calcium are not proofs for quantum non-locality. Instead, they are\\\\nnecessary evidences for the fact that “entangled” photon pairs are in the un-\\\\nentangled states |ψu〉 or |ψu′〉. According to the expression (16) for the vector\\\\npotential, in the case of “entangled” photon pairs generated in the parametric\\\\ndown conversion nonlinear optical processes with a type-II phase matching, we† and b2 †in terms of operators bv1, bv† †2, bh1 and bh2:† †\\\\nmay express the operators b1\\\\n b1 = 1† √2 (bv1 + bh2), b2 † † †= 1√2 (bv2 − bh1).† † (31)\\\\nWe have then\\\\n |ψu〉 = 1√2 |ψe〉 + 4 1(bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉.† v † v † h † h (32)\\\\nAs the component (bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉 in |ψu〉 has no contri-† † † †\\\\n v v h h\\\\nbution to the coincidence counting rate between the signals from the channel 1\\\\nand the channel 2, all apparently non-local correlations that were believed as\\\\nspecificity of photon pairs in the entangled state |ψe〉, occur also in the case of\\\\nun-entangled photon pairs in the state |ψu〉. The same conclusion holds also\\\\nfor “entangled” photon pairs generated in a radiative atomic cascade of cal-\\\\ncium Therefore no physical phenomena that necessitate introducing quantum\\\\nnon-locality for their explanation are really observed.\\\\n Einstein, Podolsky, Rosen (EPR) and Bohm had put the completeness of\\\\nquantum mechanics in contradiction to the relativistic causality by supposing\\\\nthe existence of particle pairs in entangled quantum states [14, 15]. But till\\\\nnow, such a contradiction did not occur, because no particle pairs in entangled\\\\nquantum states had been produced. Can particle pairs in entangled quantum\\\\nstates be generated ever? It is most likely not. Due to the interaction with\\\\nthe source of particle pairs, it should be impossible for the produced particle\\\\npair with different momenta to be in a quantum state with well defined mo-\\\\nmentum, such as the entangled quantum state. This observation is consistent\\\\nwith Santos’s suggestion [7] that only quantum states which do not contradict\\\\nwith locality requirement are physical states. From this point of view, the EPR\\\\nparadox is just a spectacular illustration of the restriction on quantum states\\\\nimposed by locality requirement.\\\\n\\\\nReferences\\\\n[1] J. S. Bell, Physics 1, 195 (1964)\\\\n[2] A. Aspect, P. Grangier, and G. Roger, Phys. Rev. Lett. 49, 91 (1981)\\\\n\\\\n 9\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}, {\\'class_name\\': \\'Document\\', \\'doc_id\\': \\'170e2b68-f50d-4a3b-8155-79c7b12a3da1\\', \\'embedding\\': None, \\'end_char_idx\\': None, \\'excluded_embed_metadata_keys\\': [], \\'excluded_llm_metadata_keys\\': [], \\'extra_info\\': {\\'file_name\\': \\'Quantum State of Entangled Photon Pairs\\'}, \\'metadata_seperator\\': \\'\\\\n\\', \\'metadata_template\\': \\'{key}: {value}\\', \\'mimetype\\': \\'text/plain\\', \\'relationships\\': {}, \\'start_char_idx\\': None, \\'text\\': \\'[3] A. Aspect, J. Dalibard, and G. Roger, Phys. Rev. Lett. 49, 1804 (1981)\\\\n[4] Z. Y. Ou, and L. Mandel, Phys. Rev. Lett. 61, 50 (1988)\\\\n[5] Y. H. Shih, and C. O. Alley, Phys. Rev. Lett. 61, 2921 (1988)\\\\n[6] Y. H. Shih, A. V. Sergienko, Morton H. Rubin, T. E. Kiess and C. O. Alley,\\\\n Phys. Rev. A 50, 23 (1994)\\\\n[7] E. Santos, Phys. Rev. A 46, 3646 (1992)\\\\n[8] D. V. Strekalov, A. V. Sergienko, D. N. Klyshko and Y. H. Shih, Phys. Rev.\\\\n Lett. 74, 3600 (1995)\\\\n[9] E. J. S. Fonseca, P. H. Souto Ribeiro, S. P´adua and C. H. Monken, Phys.\\\\n Rev. A 60, 1530 (1999)\\\\n[10] Z. Zhao, et al., Phys. Rev. Lett. 91, 180401 (2003)\\\\n[11] P. G. Kwiat et al., Phys. Rev. Lett. 75, 4337 (1995)\\\\n[12] F. Mandl and G. Shaw, Quantum field theory (John Wiley and Sons, Chich-\\\\n ester, 1984), Chap. 1\\\\n[13] L. Mandel and E. Wolf, Optical Coherence and Quantum Optics (Cam-\\\\n bridge University Press, 1995), p. 640\\\\n[14] A. Einstein, B. Pdolsky, and N. Rosen, Phys. Rev. 47, 777 (1935)\\\\n[15] D. Bohm, Quantum Theory (Prentice Hall, Englewood Cliffs, NJ, 1951)\\\\n\\\\n 10\\', \\'text_template\\': \\'{metadata_str}\\\\n\\\\n{content}\\'}]}, {\\'documents\\': []}]'}\n", + "--------------------------------------------------\n", + "Transition number: 2\n", + "Transition type: step\n", + "Transition output: [{'documents': []}, {'documents': [{'class_name': 'Document', 'doc_id': '5dde80d8-9fea-4e79-a8c6-4f56f6989bc5', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:quant-ph/0003151v1 31 Mar 2000\\n\\n Unconventional Quantum Computing Devices\\n\\n Seth Lloyd\\n Mechanical Engineering\\n MIT 3-160\\n Cambridge, Mass. 02139\\n\\n Abstract: This paper investigates a variety of unconventional quantum computation de-\\n vices, including fermionic quantum computers and computers that exploit nonlinear quan-\\n tum mechanics. It is shown that unconventional quantum computing devices can in prin-\\n ciple compute some quantities more rapidly than ‘conventional’ quantum computers.\\n\\n Computers are physical: what they can and cannot do is determined by the laws\\n of physics. When scientific progress augments or revises those laws, our picture of what\\n computers can do changes. Currently, quantum mechanics is generally accepted as the\\n fundamental dynamical theory of how physical systems behave. Quantum computers can\\n in principle exploit quantum coherence to perform computational tasks that classical com-\\n puters cannot [1-21]. If someday quantum mechanics should turn out to be incomplete\\n or faulty, then our picture of what computers can do will change. In addition, the set\\n of known quantum phenomena is constantly increasing: essentially any coherent quantum\\n phenomenon involving nonlinear interactions between quantum degrees of freedom can\\n in principle be exploited to perform quantum logic. This paper discusses how the revi-\\n sion of fundamental laws and the discovery of new quantum phenomena can lead to new\\n technologies and algorithms for quantum computers.\\n Since new quantum effects are discovered seemingly every day, let’s first discuss two\\n basic tests that a phenomenon must pass to be able to function as a basis for quantum\\n computation. These are 1) The phenomenon must be nonlinear, and 2) It must be coherent.\\n To support quantum logic, the phenomenon must involve some form of nonlinearity, e.g.,\\n a nonlinear interaction between quantum degrees of freedom. Without such a nonlinearity\\n quantum devices, like linear classical devices, cannot perform even so simple a nonlinear\\n operation as an AND gate. Quantum coherence is a prerequisite for performing tasks\\n such as factoring using Shor’s algorithm [10], quantum simulation a la Feynman [11] and\\n Lloyd [12], or Grover’s data-base search algorithm [13], all of which require extended\\n manipulations of coherent quantum superpositions.\\n\\n 1', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '066b2185-7165-468b-84f7-b8866f9baa4f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' The requirements of nonlinearity and coherence are not only necessary for a phe-\\nnomenon to support quantum computation, they are also in principle sufficient. As shown\\nin [14-15], essentially any nonlinear interaction between quantum degrees of freedom suf-\\nfices to construct universal quantum logic gates that can be assembled into a quantum\\ncomputer. In addition, the work of Preskill et al. [18] on robust quantum computation\\nshows that an error rate of no more than 10−4 per quantum logic operation allows one to\\nperform arbitrarily long quantum computations in principle.\\n In practice, of course, few if any quantum phenomena are likely to prove sufficiently\\ncontrollable to provide extended quantum computation. Promising devices under current\\nexperimental investigation include ion traps [5,7], high finesse cavities for manipulating\\nlight and atoms using quantum electrodynamics [6], and molecular systems that can be\\nmade to compute using nuclear magnetic resonance [8-9]. Such devices store quantum\\ninformation on the states of quantum systems such as photons, atoms, or nuclei, and\\naccomplish quantum logic by manipulating the interactions between the systems via the\\napplication of semiclassical potentials such as microwave or laser fields. We will call such\\ndevices ‘conventional’ quantum computers, if only because such devices have actually been\\nconstructed.\\n There is another sense in which such computers are conventional: although the de-\\nvices described above have already been used to explore new regimes in physics and to\\ncreate and investigate the properties of new and exotic quantum states of matter, they\\nfunction according to well established and well understood laws of physics. Perhaps the\\nmost striking examples of the ‘conventionality’ of current quantum logic devices are NMR\\nquantum microprocessors that are operated using techniques that have been refined for\\nalmost half a century. Ion-trap and quantum electrodynamic quantum computers, though\\ncertainly cutting edge devices, operate in a quantum electrodynamic regime where the\\nfundamental physics has been understood for decades (that is not to say that new and\\nunexpected physics does not arise frequently in this regime, rather that there is general\\nagreement on how to model the dynamics of such devices).\\n Make no mistake about it: a conventional quantum logic device is the best kind of\\nquantum logic device to have around. It is exactly because the physics of nuclear magnetic\\nresonance and quantum electrodynamics are well understood that devices based on this\\nphysics can be used systematically to construct and manipulate the exotic quantum states\\nthat form the basis for quantum computation. With that recognition, let us turn to\\n\\n 2', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'bcf5f805-d98f-4076-aa8c-f753cf7dbfa1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '‘unconventional’ quantum computers.\\n Perhaps the most obvious basis for an unconventional quantum computer is the use\\nof particles with non-Boltzmann statistics in a refime where these statistics play a key role\\nin the dynamics of the device. For example, Lloyd [16] has proposed the use of fermions\\nas the fundamental carriers of quantum information, so that a site or state occupied by a\\nfermion represents a 1 and an unoccupied site or state represents a 0. It is straightforward\\nto design a universal quantum computer using a conditional hopping dynamics on an array\\nof sites, in which a fermion hops from one site to another if only if other sites are occupied.\\n If the array is one-dimensional, then such a fermionic quantum computer is equivalent\\nto a conventional quantum computer via the well-known technique of bosonization. If the\\narray is two or more dimensional, however, a local operation involving fermions on the\\nlattice cannot be mocked up by a local operation on a conventional quantum computer,\\nwhich must explicitly keep track of the phases induced by Fermi statistics. As a result,\\nsuch a fermionic computer can perform certain operations more rapidly than a conventional\\nquantum computer. An obvious example of a problem that can be solved more rapidly on\\na fermionic quantum computer is the problem of simulating a lattice fermionic system in\\ntwo or more dimensions. To get the antisymmetrization right in second quantized form,\\na conventional ‘Boltzmann’ quantum computer takes time proportional to T ℓd−1 where T\\nis the time over which the simulation is to take place, ℓ is the length of the lattice and\\nd is the dimension, while a fermionic quantum computer takes time proportional to T .\\n(Here we assume that the computations for both conventional and Fermionic quantum\\ncomputers can take advantage of the intrinsic parallelizability of such simulations: if the\\ncomputations are performed serially an additional factro of ℓd is required for both types\\nof computer to update each site sequentially.)\\n As the lattice size ℓ and the dimension d grow large, the difference between the two\\ntypes of computer also grows large. Indeed, the problem of simulating fermions hopping\\non a hypercube of dimension d as d → ∞ is evidently exponentially harder on a con-\\nventional quantum computer than a Fermionic quantum computer. Since a variety of\\ndifficult problems such as the travelling-salesman problem and data-base search problem\\ncan be mapped to particles hopping on a hypercube, it is interesting to speculate whether\\nfermionic computers might provide an exponential speed-up on problems of interest in ad-\\ndition to quantum simulation. No such problems are currently known, however. Fermionic\\ncomputers could be realized in principle by manipulating the ways in which electrons and\\n\\n 3', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '53057fa4-ba5b-423b-a686-13c6953d5f12', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'holes hop from site to site on a semiconductor lattice (though problems of decoherence are\\nlikely to be relatively severe for such systems).\\n It might also be possible to construct bosonic computers using photons, phonons, or\\natoms in a Bose-Einstein condensate. Such systems can be highly coherent and support\\nnonlinear interactions: phonons and photons can interact in a nonlinear fshion via their\\ncommon nonlinear interaction with matter, and atoms in a Bose condensate can be made\\nto interact bia quantum electrodynamics (by introduction of a cavity) or by collisions. So\\nfar, however, the feature of Bose condensates that makes them so interesting from the point\\nof view of physics — all particles in the same state — makes them less interesting from the\\npoint of view of quantum computation. Many particles in the same state, which can be\\nmanipulated coherently by a variety of techniques, explore the same volume of Hilbert space\\nas a single particle in that state. As a result, it is unclear how such a bosonic system could\\nprovide a speed-up over conventional quantum computation. More promising than Bose\\ncondensates from the perspective of quantum computation and quantum communications,\\nis the use of cavity quantum electrodynamics to ‘dial up’ or synthesize arbitrary states\\nof the cavity field. Such a use of bosonic states is important for the field of quantum\\ncommunications, which requires the ability to create and manipulate entangled states of\\nthe electromagnetic field.\\n A third unconventional design for a quantum computer relies on ‘exotic’ statistics\\nthat are neither fermionic nor bosonic. Kitaev has recently proposed a quantum computer\\narchitecture based on ‘anyons,’ particles that when exchanged acquuire an arbitrary phase.\\nExamples of anyons include two-dimensional topological defects in lattice systems of spins\\nwith various symmetries. Kitaev noted that such anyons could perform quantum logic via\\nAharonov-Bohm type interactions [19]. Preskill et al. have shown explicitly how anyonic\\nsystems could compute in principle [20], and Lloyd et al. have proposed methods of\\nrealizing anyons using superconducting circuits (they could also in principle be constructed\\nusing NMR quantum computers to mock up the anyonic dynamics in an effectively two-\\ndimensional space of spins) [21]. The advantage of using anyons for quantum computation\\nis that their nonlocal topological nature can make them intrinsically error-correcting and\\nvirtually immune to the effects of noise and interference.\\n As the technologies of the microscale become better developed, more and more po-\\ntential designs for quantum computers, both conventional and unconventional, are likely\\nto arise. Additional technologies that could prove useful for the construction of quantum\\n\\n 4', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'de31f5a1-0290-45f3-b386-d02e3ed4c33c', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' logic devices include photonic crystals, optical hole-burning techniques, electron spin res-\\n onance, quantum dots, superconducting circuits in the quantum regime, etc. Since every\\n quantum degree of freedom can in principle participate in a computation one cannot a\\n priori rule out the possibility of using currently hard to control degrees of freedom such as\\nquark and gluon in complex nuclei to process information. Needless to say, most if not all\\nof the designs inspired by these technologies are likely to fail. There is room for optimism\\nthat some such quantum computer designs will prove practicable, however.\\n The preceding unconventional designs for quantum computers were based on existing,\\nexperimentally confirmed physical phenomena (except in the case of non-abelian anyons).\\nLet us now turn to designs based on speculative, hypothetical, and not yet verified phenom-\\nena. (One of the most interesting of these phenomena is large-scale quantum computation\\nitself: can we create and systematically transform entangled states involving hundreds or\\nthousands of quantum variables?) A particularly powerful hypothesis from the point of\\nview of quantum computation is that of nonlinear quantum mechanics.\\n The conventional picture of quantum mechanics is that it is linear in the sense that the\\nsuperposition principle is obeyed exactly. (Of course, quantum systems can still exhibit\\nnonlinear interactions between degrees of freedom while continuing to obey the superpo-\\nsition principle.) Experiment confirms that the superposition principle is indeed obeyed\\nto a high degree of accuracy. Nonetheless, a number of scientists including Weinberg have\\nproposed nonlinear versions of quantum mechanics in which the superposition principle\\nis violated. Many of these proposals exhibit pathologies such as violations of the second\\nlaw of thermodynamics or the capacity for superluminal communication. Despite such\\ntheoretical difficulties, it is still possible that quantum mechanics does indeed possess a\\nsmall nonlinearity, even if it currently seems unlikely. If a nonlinear operation such as\\nthat proposed by Weinberg can be incorporated in a quantum logic operation, then the\\nconsequences are striking: NP-complete problems can be solved easily in polynomial time\\n[17]. Indeed, NP-oracle problems and all problems in #P can be solved in polynomial time\\non such a nonlinear quantum computer.\\n A general proof of this result is given in [17], however, a simple argument for why\\nthis is so can be seen as follows. Suppose that it is possible to perform a non-unitary\\noperation on a single qubit that has a positive Lyapunov exponent over some region: i.e.,\\nsomewhere on the unit sphere there exists a line of finite extent along which application of\\nthe operation causes nearby points to move apart exponentially at a rate eλ∆θ proportional\\n 5', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '0777fa16-f0d4-4ad9-9877-a4abca0ba70f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'to their original angular separation δθ. Now consider a function f (x) from N bits to one\\nbit. We wish to determine whether or not there exists an x such that f (x) = 1, and if\\nso, how many such x’s there are. Using the nonlinear operation with positive Lyapunov\\nexponent, it is straightforward to construct a mapping leaves a point on the exponentially\\nexpanding line (call this point |0〉) fixed if their are no solutions to the equation f (x) = 1,\\nand that maps the point to a nearby point cos(n/2N )|0〉 + sin(n/2N )|1〉 along the line\\nif there are exactly n solutions to the equation f (x) = 1. Repeated application of the\\nnonlinear map can be used to drive the points apart at an exponentional rate: eventually,\\nat a time determined by the number of qubits N , the number of solutions n, and the rate\\nof spreading λ, the two points will become macroscopically distinguishable, allowing one\\nto determine whether or not there is a solution and if there is, how many solutions there\\nare. The map f need only be applied once, and the amount of time it takes to reveal the\\nnumber of solutions is proportional to N .\\n The fact that nonlinear quantum mechanics allows the straightforward solution of\\nNP-complete and #P problems should probably be regarded as yet another strike against\\nnonlinear quantum mechanics. Whether or not quantum mechanics is linear is a question\\nto be resolved experimentally, however. In the unlikely event that quantum mechanics\\ndoes turn out to be nonlinear, all our problems may be solved.\\n Finally, let us turn our attention to hypothetical quantum Theories of Everything,\\nsuch as string theory. Such a theory must clearly support quantum computation since it\\nsupports cavity quantum electrodynamics and nuclear magnetic resonance. The obvious\\nquestion to ask is then, does a Theory of Everything need to support anything more than\\nquantum computation? So far as experimental evidence is concerned the answer to this\\nquestion is apparently No: we have no evident reason to doubt that the universe is at\\nbottom anything more than a giant, parallel, quantum information processing machine,\\nand that the phenomena that we observe and attempt to characterize are simply outputs\\nof this machine’s ongoing computation. Of course, just how the universe is carrying out\\nthis computation is likely to remain a question of great interest for some time.\\n To summarize: Computers are physical systems, and what they can do in practice and\\nin principle is circumscribed by the laws of physics. The laws of physics in turn permit a\\nwide variety of quantum computational devices including some based on nonconventional\\nstatistics and exotic effects. Modifications made to the laws of physics have the consequence\\nthat what can be computed in practice and in principle changes. A particularly intriguing\\n\\n 6', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'd315d076-2b6e-4ec4-ade1-e18fb6c09998', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'variation on conventional physics is nonlinear quantum mechanics which, if true, would\\nallow hard problems to be solved easily.\\n\\n 7', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '7cc3c174-6e77-4928-af58-522265cace2a', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'References\\n\\n1. P. Benioff, ‘Quantum Mechanical Models of Turing Machines that Dissipate No Energy,’\\nPhysical Review Letters, Vol. 48, No. 23, pp. 1581-1585 (1982)\\n2. D. Deutsch, ‘Quantum Theory, the Church-Turing Principle and the Universal Quantum\\nComputer,’ Proceedings of the Royal Society of London, A, Vol. 400, pp. 97-117 (1985).\\n3. R.P. Feynman, ‘Quantum Mechanical Computers,’ Optics News, Vol. 11, pp. 11-20\\n(1985); also in Foundations of Physics, Vol. 16, pp. 507-531 (1986).\\n4. S. Lloyd, ‘A Potentially Realizable Quantum Computer,’ Science, Vol. 261, pp. 1569-\\n1571 (1993).\\n5. J.I. Cirac and P. Zoller, ‘Quantum Computations with Cold Trapped Ions,’ Physical\\nReview Letters, Vol. 74, pp. 4091-4094 (1995).\\n6. Q.A. Turchette, C.J. Hood, W. Lange, H. Mabuchi, H.J. Kimble, ‘Measurement of\\nConditional Phase Shifts for Quantum Logic,’ Physical Review Letters, Vol. 75, pp. 4710-\\n4713 (1995).\\n7. C. Monroe, D.M. Meekhof, B.E. King, W.M. Itano, D.J. Wineland, ‘Demonstration of\\na Fundamental Quantum Logic Gate,’ Physical Review Letters, Vol. 75, pp. 4714-4717\\n(1995).\\n8. D.G. Cory, A.F. Fahmy, T.F. Havel, ‘Nuclear Magnetic Resonance Spectroscopy: an\\nexperimentally accessible paradigm for quantum computing,’ in PhysComp96, Proceedings\\nof the Fourth Workshop on Physics and Computation, T. Toffoli, M. Biafore, J. Le˜ao, eds.,\\nNew England Complex Systems Institute, 1996, pp. 87-91.\\n9. N.A. Gershenfeld and I.L. Chuang, ‘Bulk Spin-Resonance Quantum Computation,’\\nScience, Vol. 275, pp. 350-356 (1997).\\n10. P. Shor, ‘Algorithms for Quantum Computation: Discrete Log and Factoring,’ in\\nProceedings of the 35th Annual Symposium on Foundations of Computer Science, S. Gold-\\nwasser, Ed., IEEE Computer Society, Los Alamitos, CA, 1994, pp. 124-134.\\n11. R.P. Feynman, ‘Simulating Physics with Computers,’ International Journal of Theo-\\nretical Physics, Vol. 21, pp. 467-488 (1982).\\n12. S. Lloyd, ‘Universal Quantum Simulators,’ Science, Vol. 273, pp. 1073-1078 (1996).\\n13. L.K. Grover, ‘Quantum Mechanics Helps in Searching for a Needle in a Haystack,’\\nPhysical Review Letters, Vol. 79, pp. 325-328 (1997).\\n14. D. Deutsch, A. Barenco, A. Ekert, ‘Universality in Quantum Computation,’ Proceed-\\nings of the Royal Society of London A, Vol. 449, pp. 669-677 (1995).\\n\\n 8', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a9610b7b-a167-4f21-a917-3626fbddde3b', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' 15. S. Lloyd, ‘Almost Any Quantum Logic Gate is Universal,’ Physical Review Letters,\\n Vol. 75, pp. 346-349 (1995).\\n 16. S. Lloyd, ‘Fermionic Quantum Computers,’ talk delivered at the Santa Barbara work-\\n shop on Physics of Information, November 1996.\\n 17. D. Abrams and S. Lloyd, to be published.\\n 18. J. Preskill et al., to be published.\\n19. Yu. Kitaev, to be published.\\n20. J. Preskill et al., to be published.\\n21. S. Lloyd et al. to be published.\\n\\n 9', 'text_template': '{metadata_str}\\n\\n{content}'}]}, {'documents': [{'class_name': 'Document', 'doc_id': '278bd226-32be-4488-a970-323f579ee8e1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:1707.02094v1 [quant-ph] 7 Jul 2017\\n\\n Quantum-dot based photonic quantum networks\\n Peter Lodahl\\n Niels Bohr Institute, University of Copenhagen, Blegdamsvej 17, DK-2100\\n Copenhagen, Denmark\\n E-mail: lodahl@nbi.ku.dk\\n\\n Abstract. Quantum dots embedded in photonic nanostructures have in recent years\\n proven to be a very powerful solid-state platform for quantum optics experiments. The\\n combination of near-unity radiative coupling of a single quantum dot to a photonic\\n mode and the ability to eliminate decoherence processes imply that an unprecedent\\n light-matter interface can be obtained. As a result, high-cooperativity photon-\\n emitter quantum interfaces can be constructed opening a path-way to deterministic\\n photonic quantum gates for quantum-information processing applications. In the\\n present manuscript, I review current state-of-the-art on quantum dot devices and their\\n applications for quantum technology. The overarching long-term goal of the research\\n field is to construct photonic quantum networks where remote entanglement can be\\n distributed over long distances by photons.\\n\\n 1. Introduction\\n\\n The ultimate vision of photonic quantum technology is to construct a complex quantum\\n network of stationary quantum nodes connected by flying photons in a fully quantum\\n way, i.e. quantum entanglement may become distributed. Such a new photonic\\n paradigm would have novel applications within secure quantum communication and\\n is proposed as a way of scaling up quantum computers. It is popularly referred to as the\\n ’quantum internet’ [1] and has been rooted in the atomic physics community where an\\n impressive proof-of-concept elementary two-node quantum network has been achieved\\n [2]. Solid-state alternatives to atomic single-photon emitters are attractive, since unlike\\n atoms they do not require complex laser cooling and trapping techniques. On the\\n other hand solid-state systems are often considered to be ’noisy’ in the sense that many\\n potential decoherence processes may deteriorate quantum properties. Remarkably, self-\\n assembled quantum dots (QDs) emitting single photons in the optical domain, notably\\n InGaAs QDs embedded in GaAs semiconductors, have matured dramatically within\\n the last few years. By systematically studying and combating the relevant decoherence\\n processes [3] impressive coherence has been demonstrated including near-perfect single-\\n photon indistinguishability (above 98%) of two subsequently emitted photons [4, 5] and\\n transform-limited emission lines [6]. Combined with the ability to dramatically enhance\\n light-matter interaction in photonic nanostructures [7], this enables near-deterministic', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'c6fdc95e-eabb-4b5e-a3c0-4c315d4c8f59', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 2\\n\\nsingle-photon sources with an internal collection efficiency exceeding 98 % [8]. This\\nmajor progress entails that QD single-photon sources are now gradually outperforming\\nthe traditional approaches based on atoms or spontaneous parametric down-conversion.\\nQD sources benefit from fast operation speeds, excellent stability and brightness, as well\\nas the potential scalability to multiple single photons and emitters. Notably, a number of\\nimpressive experiments have recently been implemented with QDs that have not been\\naccomplished on other platforms. They include the experimental demonstration of a\\ndeterministic entangled cluster state of strings of photons [9], boson sampling with so far\\nfive photons [10], the detection of squeezed light correlations in resonance fluorescence\\n[11], and the demonstration of an on-demand entangled photon source with higher\\nthan 90% fidelity [12]. Time is now to build on these and other achievements in order\\nto scale QD photonic quantum technology and construct large and complex quantum\\narchitectures for quantum-information processing applications.\\n The present manuscript reviews the current state-of-the-art on quantum photonics\\nbased on QD photon emitters towards the overarching goal of constructing photonic\\nquantum networks. At present, a number of basic functionalities have been\\nsuccessfully demonstrated with generally impressive performance. These may constitute\\nfundamental building blocks of photonic quantum networks. In that sense a current\\nmajor challenge for theoretical quantum physicist is to develop resource efficient\\narchitectures tailored to the specific quantum hardware available, i.e., addressing how\\nthe basic ’puzzle pieces’ can be combined. Figure 1 illustrates the ’puzzle pieces’ already\\nbeing developed for the QD platform, and that will be considered here. They include\\nsingle-photon sources, single-photon nonlinearity, photonic circuitry, efficient-coupling\\nto optical fibers, on-chip single-photon detectors, and multi-emitter coupling.\\n QDs enable two different types of quantum resources: they may be employed\\nas a source of single photons or alternatively a single spin trapped in a QD may be\\nmanipulated as a qubit. Figure 2 illustrates these two approaches. In the former case a\\nsingle electron-hole pair is created in a QD by either optical excitation or by controllably\\ntunneling carriers into the QD with the application of an electric field. The electron-\\nhole pair subsequently recombines whereby a single photon is emitted by spontaneous\\nemission within a lifetime of typically 1 ns for a QD in a bulk medium. In the latter\\napproach a single carrier (electron or hole) is prepared in the QD by tunneling. The\\ncarrier spin is coupled to light since a photon may subsequently be absorbed by the QD\\ncreating a negatively (positively) charged exciton for the case of an initial electron (hole).\\nDepending on the operational condition, the spin may have a coherence time of up to\\n1 μs (for holes) [13, 14], which means that in principle many quantum operations can\\nbe carried out with fast optical control techniques before decoherence sets in. For some\\nquantum-information applications, however, longer spin coherence times are required,\\ne.g., in matter based quantum-repeater architectures. To this end, the QD platform\\nmust be interfaced with long-lived quantum memories based on, e.g., defect centers\\nin diamond or atomic ensembles [15]. Such hybrid quantum network architectures are\\noutside the scope of the present Review.', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a19b0d3b-ebad-4d36-a615-6a4ee63a39ad', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 3\\n ~l ~\\n Nonlinearity Processing\\n wn\\n Fiber Out-coupling Single photons Detection Dipole-dipole interaction\\nFigure 1: Illustration of basic functionalities that can be implemented with QDs and\\nphotonic nanostructures. Left panel: efficient outcoupling taper sections from photonic\\nnanostructures to optical fibers can be engineered to obtain highly efficient outcoupling\\nof single photons. Center panel: a single QD in nanophotonic waveguide or cavity can be\\nused as a highly efficient and coherent source of single photons. Two sources are shown\\nin order to illustrate potential scalability of the approach. Right panel: a single QD\\nefficiently coupled to a waveguide may be employed as a single-photon nonlinearity\\n(upper left), complex photonic circuits may be constructed on a photonic chip for\\nquantum processing of photons (upper right), single-photon superconducting detectors\\nmay be implemented on-chip for highly efficient detection (lower right), and multiple\\nQDs may be coupled by engineering the dipole-dipole interaction in a nanophotonic\\nwaveguide. The figure is a courtesy of Sahand Mahmoodian.\\n 2 2\\n Ia) hole k(b)\\n electron M MM\\nFigure 2: Photon and spin qubits generated with QDs. The light grey areas illustrate\\nthe regions of InAs embedded in dark grey GaAs regions thereby creating an energy\\nwell potential for both electrons and holes. (a) An electron-hole pair trapped in the\\nQD may recombine by emitting a single photon. (b) A single electron (or hole) trapped\\nin the QD constitutes a spin qubit (orange arrow) that may be manipulated by optical\\nmethods.', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '54241ad6-863a-4e89-b9c2-25ea50cede61', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 4\\n\\n2. Single-photon sources\\n\\nA QD efficiently coupled to a nanophotonic cavity or waveguide offers a promising\\nroute towards a deterministic single-photon source. Early pioneering work showed that\\nPurcell enhancement in an optical cavity may be employed to both improve brightness\\nand coherence of the source [16]. Ideally a useful single-photon source emits an optical\\npulse containing a single photon in a useful optical mode (e.g., into an optical fiber)\\nevery time the QD is triggered by either an optical or electrical pulse. Furthermore,\\nmost applications in quantum-information processing require that the emitted photons\\nare fully coherent, which entails that decoherence processes occurring on a time scale\\nof the radiative decay of the QD must be eliminated. Thus, three major figures-of-\\nmerit need to be optimized: i) the single-photon purity, ii) the single-photon coupling\\nefficiency, and iii) the indistinguishability of the emitted photons.\\n Re i): The discrete level structure of self-assembled QDs having widely separated\\noptical transitions implies that they are capable of emitting high-purity single photons\\n[17]. This is gauged in a Hanbury Brown - Twiss (HBT) correlation experiment by\\nrecording the multi-photon emission probability. By using samples with a low QD\\ndensity and implementing (quasi)-resonant excitation to selectively excite only a single\\nQD, excellent single-photon purity can be obtained at the level of g(2)(0) ≤ 0.1%, cf.\\nFig. 3.\\n Re ii): The overall coupling efficiency determines the brightness of the single-photon\\nsource and is comprised of several factors [7]: the excitation and single-photon emission\\nprobability of the QD, the efficiency with which the emitted photons are channeled to a\\nsingle mode (the β-factor), and the transfer efficiency to a low-loss propagating mode,\\ne.g., an optical fiber. Significant progress has been reported on all three engineering\\ntasks: resonant π-pulse excitation allows deterministically preparing a single exciton in\\na QD [18] and electrically gated structures eliminate blinking between different exciton\\ncomplexes [19]. Embedding QDs in photonic nanostructures allows reaching a near-\\nunity β-factor, which has been obtained in nanophotonic waveguides [8, 20] and cavities\\n[4]. Finally, a variety of approaches and designs can be implemented for transferring\\nthe collected photons to an optical fiber using, e.g., tailored gratings for coupling light\\nguided on planar structures vertically off the chip, or waveguide taper sections designed\\nto couple directly to a fiber either by direct or evanescent coupling [21, 22, 23].\\n Re iii): The indistinguishability of an emitted photon is determined by the amount\\nof decoherence taking place within the emitter decay time. With a typical decay time\\nof nanoseconds, the primary decoherence source is coupling of the QD to phonons\\nand potentially photon jitter induced by relaxation processes originating from non-\\nresonant excitation schemes [24]. The latter may be overcome by strict resonant\\nexcitation. Phonon interactions at the contrary are unavoidable and lead to two\\neffects: wide phonon sidebands and residual broadening of the zero-phonon line. The\\nformer contribution (containing typically about 10 % of the the total emission at\\nlow temperatures [7]) can readily be filtered away either by an external filter thereby', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'de71718d-112b-478e-b755-5c7d40825e52', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 5\\n\\nreducing the overall efficiency or more favorably by implementing narrow-band Purcell\\nenhancement. Hence the broadening of the zero-phonon line remains the fundamental\\ndecoherence mechanism. An in-depth theoretical study of this process including the\\nrole of the dimensionality of the photonic nanostructure can be found in Ref. [25].\\nImportantly a clear route to indistinguishable photons with near-unity visibility can be\\nlaid out, which has also been confirmed experimentally [4, 5, 26].\\n The previous discussion illustrates that QDs are capable of producing a truly on-\\ndemand source of single photons by implementing, in a single device, the functionalities\\ndemonstrated so far only in different experiments. It is likely that such a source will\\nbe developed experimentally soon. In particular, it is remarkable that decoherence\\nprocesses can be overcome in a solid-state system to such an extent that near-unity\\nindistinguishability between subsequently emitted photons from the same QD can be\\nobtained. Next step is to convert a single QD source generating photons as ”pearls\\non a string” into a de-multiplexed source delivering many identical single photons\\nsimultaneously in individual modes. Such a source can be constructed by cascading\\nelectro-optical switches and coupling the photons into different optical fibers in order\\nto overcome the time delay between subsequently emitted photons, cf. Fig. 4(a).\\nTo this end, the demonstration of near-transform-limited QD emission lines [6] has\\nthe remarkable consequence that a single QD can emit thousands of indistinguishable\\nphotons thereby providing a huge quantum resource. Indeed recent demonstration of\\nindistinguishability exceeding 90 % between two photons separated by more than 1000\\nemission events has proven this explicitly [27].\\n\\n3. Integrated quantum photonics\\n\\nAs detailed in the previous section, currently the most reliable approach to generate\\nmultiple single photons utilizes a single integrated QD source where the photons\\nare coupled off-chip to an optical fiber, and highly-efficient bulk optical components\\nimplemented for switching and routing, cf. Fig. 4(a). Nonetheless, the potential benefits\\nof integrating functionalities on-chip are many in terms of stability, ease of operation,\\nlow loss, and speed [28]. Figure 4(b) shows an architecture for an integrated de-\\nmultiplexed source of single photons based on a single QD in a planar nanophotonic\\nwaveguide platform. To implement this scheme requires the development of high-\\nefficiency and fast switches, and multi-port waveguide-fiber interfaces. The former\\nrequires the development of new devices and functionalities on-chip where so-far the\\nmost common approach for reconfigurable circuitry has been the application of thermo-\\noptic phase shifters [29]. De-multiplexing requires fast switches, and two promising\\napproaches include electro-optical modulation implemented directly in the host material\\nof the QD (i.e. GaAs) [30, 31] or electro-mechanical coupling [32].\\n The crucial parameter determining the size of the photonic resource that can be\\ngenerated with a de-multiplexed single-photon source is the overall efficiency, including\\nswitching, propagation, and coupling efficiencies. Consequently the rate of N-photon', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '5982b6b2-feb7-48e0-9d25-1a0c89baaf44', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 6\\n 15 pairs taper WG\\n ~Fiber\\n pairs\\n Tum\\n Wch\\n HBT\\n 3 0.8 = Channel\\n 1 WG\\n Fiber taperWG\\n 0.4-\\n 0.2 300 r 140 nm\\n \"37.5 25.0 -12.5 Delay (ns) 12.50,0 25,0 37.5 a\\n 1 0.2 Az\\n 3 060.81.0 HOMpol:\\n 1 0.8 inputNanobeam waveguidePC 90.10 samplecryostat 0.8\\n 0.6 0.6\\n 0.0 0.4 0.4 ]\\n ] 1.00.8\\n 0.6 NU pOlvIpot 1 0.2 Photonic-crystal waveguide 0.2\\n 0.4\\n 0.2 1 0.60.4\\n 0.2 0.4\\n 0.6\\n 0.2\\n Time delay (ns) 850 900 950 1000\\n Wavelength (nm)\\nFigure 3: Purity, indistinguishability, and out-coupling efficiency of QD single-\\nphoton sources. Left column: Examples of HBT and Hong-Ou-Mandel (HOM)\\nindistinguishability measurements by implementing resonant excitation on a QD in\\na micropillar cavity (upper figure shows the cavity structure). g(2)(0) = 0.009 and\\na single-photon indistinguishability of 96.4% is extracted from the two sets of data.\\nFigures reproduced from Ref. [5]. Right column: Examples of devices made for out-\\ncoupling single photon from a QD in a planar nanophotonic waveguide to an optical\\nfiber with high efficiency by tailored evanescent coupling. The upper panel is reproduced\\nfrom Ref. [21]. Lower panel: application of this method to couple out single photons\\nfrom high β-factor nanophotonic waveguides, i.e., nanobeam waveguides and photonic-\\ncrystal waveguides. By recording the reflection and transmission spectrum of the device,\\na chip-to-fiber coupling efficiency of > 80% is obtained. Data reproduced from Ref. [23].\\n\\ngeneration is RN = Rpump ×ηN /N , where η is the overall transmission efficiency from the\\nsource to the fiber and Rpump is the repetition rate of the excitation laser. Exemplarily', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '22f52865-e757-49a9-8244-cad99bf66572', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 7\\n PC (b) Electrically-controlled\\n switches\\n de-muitipieett QD 8-98% Array of delay fibers\\n 2\\n Oei\\n Photonic crystal\\n ultte ~fow-Ioss deleco e waveguide\\n photonic circuit\\nFigure 4: Illustrations of schemes for de-multiplexing a single-photon source. (a)\\na five-photon source obtained by cascading four Pockels cells (PCs) and polarizing\\nbeamsplitters to couple subsequently emitted photons from the QD into different optical\\nfibers with different delay lengths. This de-multiplexed source is applied for proof-\\nof-concept boson sampling. Figure reproduced from Ref. [10]. (b) Architecture\\nof an integrated de-multiplexed source based on a QD coupled with high efficiency\\nto a photonic-crystal waveguide. The emitted photons are transferred to dielectric\\nwaveguides and subsequently routed by electrical switches and coupled off-chip to\\ndifferent fiber delays in order to compensate for the time delay in-between photons.\\nThe figure is a courtesy of Leonardo Midolo.\\n\\nconsidering η = 50% and Rpump = 80 MHz (corresponding to the repetition rate of a\\nTi:Sapph. laser) corresponds to a 10-photon generation rate of R10 ∼ 8 kHz, which\\nillustrates the promising prospects of this approach while posing clear benchmarks for\\nthe efficiency of the applied switching and coupling technology.\\n Integrating a de-multiplexed source on a chip is one immediate goal. Another\\nimportant task is to develop complex integrated tunable photonic circuits that can\\nprocess the generated single photons leading to applications for quantum simulations\\n[33]. Current state-of-the-art of this technology is a six-mode reprogrammable circuit\\ncapable of implementing universal high-fidelity quantum gates [29]. Considerable effort\\nis directed to the scaling of these circuits in working towards a fully integrated system\\nwhere photon source and processing circuit would be implemented on a single photonic\\nchip. However, implementing on-chip the relatively long optical delays (typically tens of\\nnanoseconds) required to interfere subsequently emitted photons, poses a challenge for\\nthis approach in requiring slow-light optical buffers. Consequently at present it seems\\nmost realistic to consider approaches based on two separate chips, connected by optical\\nfibers, where one chip constitutes the source and the other chip the quantum processor.\\n The operation wavelength of a quantum photonics processor is an essential\\nparameter. The preferred operation wavelength is within the telecom C-band (1.55 μm),\\nwhere advanced high-performance optical components and fiber technology developed\\nfor telecom industry are present. The natural emission wavelength of self-assembled high\\nquality QDs is around 950 nm and while significant efforts are taken to develop QDs in\\nthe telecom bands as well [34, 35] the excellent performance in terms of coherence and\\nefficiency required for quantum technology has not yet been achieved. An alternative', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '6005404e-7547-45e3-b282-52c7be36abcf', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 8\\n\\napproach employs frequency down-conversion of single photons from the near-infrared\\nto the telecom band. Noise free conversion of single photons from a QD with an\\nefficiency exceeding 30% has been demonstrated [36], which could be further increased by\\nimproving the in- and out-coupling efficiency through the nonlinear conversion crystal.\\nThe two-chip approach alluded to above has the asset that the frequency conversion\\nstep could naturally be incorporated in between the source chip and the processing\\nchip. Furthermore, implementing frequency conversion of QD photons also has another\\nadvantage, since it provides a way of overcoming spectral inhomogeneities of photons\\nemitted by different QDs by transducing them to the same wavelength in the C-band\\nwith the implementation of a tunable pump laser. This could prove a powerful way of\\nscaling up QD systems to couple multiple emitter qubits (e.g., spins) in addition to the\\nmultiple photon qubits already considered.\\n Finally, another essential requirement is the ability to detect optical photons with\\nvery high efficiency. Very significant progress has been obtained in recent years with the\\ndevelopment of superconducting nanowire single-photon detectors [37], where the single-\\nphoton detection efficiency today approaches 100% and the speed is compatible with QD\\nsources. Importantly these detectors can naturally be integrated on the planar GaAs\\nplatform containing QDs [38], which means that photon source, circuit, and detection\\ncould potentially be integrated on a single chip, which may pave the way to ultimately\\nlow-loss photonic quantum nodes.\\n\\n4. Single-photon nonlinearity\\n\\nThe previous sections concerned the generation of single photons and their subsequent\\nquantum interference in photonic circuits. Many quantum-information applications\\nof photons require generating photon-photon interactions. Generally photons interact\\nweakly meaning that standard nonlinearities are typically too weak to be operational\\nat the level of single photons. However, the efficient coupling of single QDs to photonic\\nnanostructures imply that such a system may be exploited to mediate a giant nonlinear\\nresponse operational at the level of single photons. Two different approaches can be\\ntaken to reach a giant nonlinearity with a QD or any other quantum emitter: the QD\\ncan be strongly coupled to a cavity and the nonlinearity of the Jaynes-Cummings ladder\\nexploited [39, 40], or a QD very efficiently coupled to a single optical mode in a cavity\\n[41, 42] or waveguide [43] may be used as a saturable nonlinearity. The latter approach\\nis beneficial for ease of operation, since it is less sensitive to the exact tuning of the\\nwavelength of the QD. Furthermore, the saturable QD nonlinearity generally can be\\noperated at a weaker incident photon flux [43]. The key operational principle of the QD\\nnonlinearity is as follows: a narrow-band pulse containing a single photon is reflected\\nfrom a QD in a waveguide due to destructive interference of forward scattering and the\\nincoming field. The efficient coupling (high β-factor) and coherent interaction, which\\ncan be achieved with QDs, implies that the interaction between the photon and the\\nQD is deterministic. Since a two-level emitter can only scatter one photon at a time,', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'c1b95287-4dfa-411d-8269-8450b9dac1db', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 9\\n\\ntwo photons have an increased probability to be transmitted past the QD leading to a\\nphoton sorting process. From an experimental point of view this type of nonlinearity\\nis attractive since no active control over the quantum state of the QD is required,\\ni.e., the QD is merely exploited as a passive scatterer. On the other hand, the photon\\nsorting is not ideal even for a perfect coupling efficiency. This implies that while a single-\\nphoton component of a pulse may be deterministically reflected, two- and higher-photon\\ncomponents are only partially transmitted and in this process entanglement is generated\\nbetween the two photons [44]. One approach of overcoming this limitation and obtain\\ndeterministic photon sorting has been suggested by combining the QD nonlinearity with\\na nonlinear spatio-temporal mode selector [45]. Such an approach may be applied for\\nconstructing a resource-efficient Bell-state analyzer [44, 45], cf. Section 7. It should be\\nmentioned that having a three-level emitter with coherent control of the level populations\\nin the waveguide opens a range of additional opportunities. Such level schemes can be\\nimplemented with charged QDs, cf. the description in the next section. One exciting\\nproposal is that of a photonic transistor controlled by only a single photon [46].\\n\\n5. Spin-photon interfaces\\n\\nIntroducing a single spin in a QD leads to a range of additional opportunities. It grants\\naccess to two separate ground states (spin up or down) that can be exploited as a\\nquantum memory to encode a qubit. Either single holes or electrons can be employed\\nand may be deterministically prepared by controlling the tunnel barriers in electrically\\ncontacted semiconductor structures. Various level structures can be implemented by the\\nuse of Zeeman tuning with a magnetic field, and spin initialization, manipulation, and\\nread-out can be achieved rapidly with short optical pulses (typically in the nanosecond\\nrange), cf. Ref. [19] for a detailed review of the physics of single spins in QDs.\\nThe spin lifetime can be in the range of milliseconds or longer in a large magnetic\\nfield, which is determined by phonon-mediated spin relaxation processes. However,\\nin quantum applications the coherence time of the spin is the essential parameter.\\nA coherence time of up to T 2∗ > 460 ns has been reported for hole spins that has\\nthe advantage compared to electrons that they do not suffer from the Fermi contact\\nhyperfine interaction with nuclear spins [14]. Importantly the spin coherence time can\\nbe several orders of magnitude longer than the spin manipulation time, which means\\nthat multiple quantum gate operations could be implemented within the spin coherence\\ntime. Unravelling spin decoherence mechanisms in QDs is a research topic in itself and is\\noutside the scope of the present review. For a thorough discussion, see Refs. [19, 47]. It\\nis anticipated that further research efforts will enable increasing the spin coherence time\\nfurther. Nonetheless, it seems unlikely that it will be possible to extend the coherence\\ntime by many orders of magnitude in present QD systems. In particular, long-range\\nquantum repeater protocols would require millisecond range quantum memories. This\\nentails a hybrid approach where the single photons generated by QDs are interfaced\\nwith long-lived quantum memories, e.g., vacancy centers in diamond [48], ions [49], or', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '093afd39-07a0-4589-a0c9-eb679ce77c2f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 10\\n\\n potentially mechanical systems.\\n Considering a charged exciton in an external magnetic field, various four-level\\n emitter schemes can be engineered with two ground states corresponding to either\\n (pseudo)spin up or down of the electron (hole). The recombination of a charged exciton\\n leads to the emission of polarized photons, where the polarization of the photon is\\n correlated with the resulting spin state. In this way entanglement between the QD\\n spin and the polarization of the emitted photon can be generated [13, 50] where\\n in general hyper-entanglement between spin, polarization, and frequency is obtained.\\n Subsequently the information in one of the variables can be erased to create a Bell state.\\n This has been the resource enabling quantum teleportation between a single photon and\\n a solid-state spin [51]. A further extension of this experiment was the demonstration of\\n heralded entanglement between two spatially separate hole spins in QDs [52], cf. Fig. 5\\n for the experimental layout.\\n Another possibility is to couple QD spins directly to photon path by exploiting\\n the concept of chiral light-matter interaction [53]. By tailoring the local electric field\\n in a nanophotonic waveguide to match the in-plane circular dipole moment of one of\\n the charged exciton transitions, it is possible to induce deterministic directional photon\\n emission [54], i.e. σ± transitions emit in different directions. This effect was used\\nto demonstrate an interface between QD spin and the photon path [55]. Based on\\nthis functionality a photonic quantum network architecture was put forward and its\\nfeasibility evaluated for QDs in nanophotonic waveguides [56], cf. Fig. 5.\\n\\n6. Coupling of multiple quantum dots\\n\\nWe have seen in previous sections how a single QD deterministically coupled to a single\\noptical mode may be exploited as a source of multiple photonic qubits potentially\\ngenerating a large quantum resource. It is natural to consider the option of scaling\\nfurther up by coupling multiple QDs. This is generally a demanding task, as it is\\nthe case for any solid-state optical emitter, since inhomogeneous broadening of a QD\\nensemble (typically at the level of THz, which should be compared to the sub-GHz\\nhomogenous linewidth of a QD) implies that significant spectral mismatch would need\\nto be overcome. Various tuning methods of QDs have been implemented including\\nelectric-field tuning [57] and strain tuning [58] that needs to be implemented locally to\\neach individual QD. Eventually not only the central emission frequency of the different\\nemitters must be controlled, but the complete photon pulses need to be matched in\\norder to faithfully couple multiple QDs. An important figure-of-merit of the success is\\ngauged in a quantum-interference experiment between two separate QD single-photon\\nsources, where an interference visibility of 93% has been reported in a weak-excitation\\nregime [59], which is a highly encouragin result for this approach.\\n Given the ability to radiatively couple two or more QDs, a photonic nanostructure\\ncan be constructed to enhance and tailor the coupling. Multiple QDs coupled by\\ndipole-dipole interaction lead to the formation of collective quantum states that can', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '39ce6fe7-fe46-40b9-9d03-35a978fc7363', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 11\\n Entanglement and\\n non-local measurement\\n Spin preparation and Spin preparation and local (a) Chiral element\\n local measurements (QDI) Ti Sapphiremeasurements (QD2)\\n laser\\n Diode las 0 +\\n Diode lasers /1)\\n EOM (b) Network node\\n EOMs EOMs\\n Intranode parity measurement Internode entanglement\\n BSI Detector 0 Detector\\n Emitter 0\\n Fiber Emitter\\n Single photon Communication photon\\n source\\n N Gratings BS2\\n (c) Linked Nodes\\n filters\\n Single-photon detectors Photon routing\\n QDI OD2\\n 5 m\\nFigure 5: Left panel: experimental setup employed for the generation of remote\\nentanglement between two QD hole spins in two different cryostats separated by\\n5 m. Figure reproduced from Ref. [52]. Right panel: Architecture of a photonic\\nquantum network based on chiral photon-emitter elements (a) embedded in on-chip\\ninterferometers constituting fundamental quantum nodes (b). The quantum nodes are\\nlinked together by photons for remote entanglement distribution (c). Figure reproduced\\nfrom Ref. [56].\\n\\nhave sub- or super-radiant character. In photonic nanostructures, the dipole-dipole\\ninteraction can be significantly extended beyond the behavior found in a homogenous\\n(3D) medium, where the coupling falls off rapidly with distance d (scales as d−3). In\\na 1D waveguide with radiative coupling to the waveguide as the dominating process,\\ndipole-dipole interaction may be infinitely extended, i.e. in practice it is limited by\\nthe absorption or scattering loss of the guided mode. Controlling the distance between\\nmultiple emitters enables the switching between sub- and super-radiant behavior. Apart\\nfrom the fundamental interest, the long-range dipole-dipole coupling may be exploited\\nto construct quantum gates between stationary qubits [60]. Introducing chiral coupling\\nopens new opportunities by the ability to engineer the directional coupling between\\nemitters. This may lead to novel opportunities for quantum simulators and for entering\\nnew regimes of coupled photons and emitters. For instance directional coupling has been\\nfound to lead to the formation of quantum dimers in the steady state of the system [61].\\nAnother approach to scaling to multiple emitters exploits tunnel coupling between QDs\\ngrown in close mutual vicinity. With such an approach advanced level structures can\\nbe engineered in these QD molecules where it has been demonstrated that the spin\\ncoherence time can be significantly extended [62].', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '5516db24-13d0-408f-a09f-df9415a927b9', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 12\\n\\n7. Quantum network architectures\\n\\nWe have reviewed various basic quantum functionalities and hardware that can be\\nimplemented with the use of QDs embedded in modern nanophotonic structures. It\\nis a pertinent task, to consider what applications and actual architectures can be\\nimplemented with these quantum resources and how to make them most resource\\nefficient. This section briefly highlights some of the opportunities and future directions.\\nA general observation, as discussed in detail above, is that QD systems can generate\\nmany single-photon qubits, while the scaling up to many matter qubits is expected to\\nbe much more challenging. It is important to have these general guidelines in mind\\nwhen designing resource efficient quantum network architectures that is tailored to the\\nquantum hardware based on QDs.\\n As discussed in the present Review, QDs in nanophotonic structures are very well\\nsuited for generating highly coherent single photons on demand at a high repetition\\nrate. Such a source can generate thousands of indistinguishable photons [27] that if\\nefficiently routed could be an important photonic resource for quantum simulations [33].\\nFurthermore, recent developments on how to efficiently fuse photonic qubits with linear\\noptics have the renewed excitement on how to construct single-photon based quantum\\ncomputing [63].\\n Introducing a single-photon nonlinearity to the toolbox opens for a range of\\nadditional possibilities. The most simple case is that of a two-level emitter, and here\\ndistortion of the optical pulses associated with photon-emitter bound state contributions\\nhas been interpreted as essentially a ”no-go theorem” for quantum computation [64].\\nOne work around, however, has been identified where the pulse distortion is actually\\nexploited such that one- and two-photon pulses are scattered into orthogonal spectral-\\ntemporal modes that can subsequently be separated, e.g., by nonlinear frequency\\nconversion [45]. With such a functionality all path-encoded photonic Bell states can\\nbe deterministically measured exploiting only four non-linear interaction process, linear\\noptics, and photon detection, cf. Fig. 6. Based on a Bell-state analyzer, quantum\\ncomputing can in principle be implemented by teleportation based gates and linear\\noptical quantum computing [65]. Alternatively, a photonic CZ gate can be directly\\nbased on the two-level emitter nonlinearity. However, in this case the pulse distortion\\nintroduced by photon scattering has to be undone by scattering an additional time on\\nthe emitter. However, since this scattering process is not time symmetric the photon\\npulse would need to be reverted in time in between the two scattering process. Such a\\ntime reversal of the photon pulses could in principle be obtained by storing, reverting,\\nand releasing photon pulses in a gradient-echo quantum memory.\\n Having access to a 3-level Λ-scheme with two stable ground states leads to many\\nmore opportunities since now the emitter can store a qubit for extended times. Coherent\\ncontrol of the qubit state enables implementing a Duan-Kimble CNOT gate for photons\\nwhere two subsequently interacting photons are entangled by their successive interaction\\nwith the same emitter [66]. Another related approach employs single-photon Raman', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'd1065b19-dd0a-4177-9589-f30e9ba2ae04', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 13\\n 1 photon\\n TLS SFG components\\n 2 photon\\n components\\n classical dichroic\\n pump splitter\\n Q1\\n Q2\\nFigure 6: Illustration of a deterministic Bell-state analyzer based on single-photon\\nnonlinearity, frequency conversion, and linear optics. Upper plot: a deterministic two-\\nlevel scattering (TLS) process induced, e.g., in a nanophotonic waveguide may be\\noperated such that one and two photons are scattered to orthogonal spatio-temporal\\nmodes. The two modes can subsequently be selected by implementing active filtering,\\ne.g. nonlinear sum-frequency generation (SFG) where the spatio-temporal mode of the\\nclassical pump is matched to, e.g., the one-photon component of the scattered light.\\nConsequently, only the single-photon component is frequency converted and can be\\nseparated from the two-photon component by a dichroic splitter. Lower plot: optical\\ncircuit containing four photon-sorting elements that is capable of determining all four\\npath-encoded Bell states by photon coincidence detection. Figure reproduced from Ref.\\n[45].\\n\\ninteraction to deterministically swap the quantum state of the emitter and a photon\\n[67, 68]. A full architecture of a quantum network based on QDs chirally coupled to\\nnanophotonic waveguides has been put forward in Ref. [56], cf. Fig. 5. Here the very\\nlarge cooperativity obtainable with QDs leads to a gate fidelity approaching unity for\\nexperimentally realistic parameters.\\n Another highly exciting direction is to exploit a single QD efficiently coupled to a\\nnanophotonic waveguide to directly produce entangled photons. To this end, Lindner', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a5cf3e02-ee6b-47ea-9b4d-e330002f40f5', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 14\\n\\nand Rudolph have put forward the proposal of a photonic ”cluster state machine gun”\\nbased on a QD that could potentially producing long strings of photons in an entangled\\ncluster state [69]. In this proposal, a single QD spin is brought into a superposition\\nstate of up and down and optically excited to a superposition of charged exciton states.\\nSubsequently the QD decays by spontaneous emission of a single photon thereby creating\\nan entangled state between the polarization of the emitted photon and the spin state.\\nThe excitation-emission process can be repeated multiple times in which case a large\\nGHZ photonic state is generated. If π/2 rotations on the QD spin are carried out\\nin-between photon emissions, an entangled photonic cluster state is created. A QD\\nembedded in a nanophotonic waveguide could potentially serve as a source of large 1D\\ncluster states, where the cluster size is determined by the number of emission events\\n(determined by the QD lifetime that is typically ∼ 100 ps in a nanophotonic structure)\\nthat can take place within the coherence time of the spin (up to ∼ μs). A pioneering\\nexperiment of photonic cluster state generation with a QD source has recently been\\nreported [9], which validates the approach.\\n Cluster states have attracted a lot of attention since it was realized that they enable\\nuniversal quantum computing. The general philosophy behind such one-way quantum\\ncomputing architectures [70] is that a large scale entangled state is generated ”up front”\\nand computation carried out by single-qubit operations induced by measurements.\\nImportantly, quantum computing requires access to 2D photonic cluster states as\\nopposed to the 1D cluster states considered above. Several proposals of how to\\nachieve this with QDs have been put forward. One approach is to optically couple\\ntwo QDs [71], which requires local tuning of the two QDs into mutual resonance. An\\nalternative proposal has been put forward based on a single quantum emitter, where the\\nemitted photons are controllably coupled back to interact with the emitter and through\\nthis process generates the 2D cluster [72]. Such a ”one-emitter quantum-computer\\narchitecture” is almost ideally suited for implementation based on QDs in nanophotonic\\nstructures. Another exciting potential applications of photonic cluster states would be in\\nquantum repeater protocols without the necessity of a matter-based quantum memory\\n[73, 74]. Such an approach would potentially overcome the main bottleneck of the QD\\nbased platform for quantum networks resulting from the relatively short-lived quantum\\nmemory offered by the QD spin.\\n\\n8. Conclusions\\n\\nQDs in photonic nanostructures constitute an unprecedent photon-emitter interface at\\noptical frequencies. The recent significant progress implies that advanced quantum-\\ninformation processing protocols now become within experimental reach in the overall\\nquest towards constructing photonic quantum networks. Such a distributed photonic\\nquantum network would open new avenues for secure quantum communication and\\nultimately provide a way of scaling up quantum computers. It is currently an exciting\\nand timely research topic to identify how the emerging quantum hardware can most', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '2ba64cf8-ce27-4486-8bc8-6dc319c814d7', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 15\\n\\nefficiently be ’put to use’ by laying out realistic and resource efficient quantum network\\narchitectures. Such a research programme requests a close interplay between theory and\\nexperiment in a continuous effort on how to exploit new quantum technology.\\n\\n9. Acknowledgements\\n\\nIt is a pleasure to acknowledge all the excellent colleagues who have contributed to my\\nunderstanding of solid-state photonic quantum networks including A.S. Sørensen, R.J.\\nWarburton, T.C. Ralph, and A.G. White, as well as all past and present members of\\nthe Quantum Photonics Group at the Niels Bohr Institute, University of Copenhagen.\\nI thank Nir Rotenberg for proof reading the manuscript. I gratefully acknowledge\\nfinancial support from the European Research Council (ERC Advanced Grant SCALE ),\\nInnovation Fund Denmark (Quantum Innovation Center Qubiz ), and the Danish Council\\nfor Independent Research.\\n [1] H. J. Kimble, The quantum internet, Nature 453, 1023 (2008).\\n [2] A. Reiserer and G. Rempe, Cavity-based quantum networks with single atoms and optical photons,\\n Rev. Mod. Phys. 87, 1379 (2015).\\n [3] A.V. Kuhlmann, J. Houel, A. Ludwig, L. Greuter, D. Reuter, A.D. Wieck, M. Poggio, and R.J.\\n Warburton, Charge noise and spin noise in a semiconductor quantum device, Nature Phys. 9,\\n 570 (2013).\\n [4] N. Somaschi, V. Giesz, L. De Santis, J. C. Loredo, M. P. Almeida, G. Hornecker, S. L. Portalupi,\\n T. Grange, C. Anton, J. Demory, C. Gomez, I. Sagnes, N. D. Lanzillotti-Kimura, A. Lema ˜Atre,\\n A. Auffeves, A. G. White, L. Lanco, and P. Senellart, Near-optimal single-photon sources in the\\n solid state, Nature Phot. 10, 340 (2016).\\n [5] X. Ding, Y. He, Z.-C. Duan, N. Gregersen, M.-C. Chen, S. Unsleber, S. Maier, C. Schneider, M.\\n Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, On-demand single photons with high extraction\\n efficiency and near-unity indistinguishability from a resonantly driven quantum dot in a\\n micropillar, Phys. Rev. Lett. 116, 020401 (2016).\\n [6] A.V. Kuhlmann, J.H. Prechtel, J. Houel, A. Ludwig, D. Reuter, A.D. Wieck, and R.J. Warburton,\\n Transform-limited single photons from a single quantum dot, Nature Comm. 6, 8204 (2015)\\n [7] P. Lodahl, S. Mahmoodian, and S. Stobbe, Interfacing single photons and single quantum dots\\n with photonic nanostructures, Rev. Mod. Phys. 87, 347 (2015).\\n [8] M. Arcari, I. S¨ollner, A. Javadi, S. Lindskov Hansen, S. Mahmoodian, J. Liu, H. Thyrrestrup,\\n E. H. Lee, J. D. Song, S. Stobbe, and P. Lodahl, Near-unity coupling efficiency of a quantum\\n emitter to a photonic crystal waveguide, Phys. Rev. Lett. 113, 093603 (2014).\\n [9] I. Schwartz, D. Cogan, E.R. Schmidgall, Y. Don, L. Gantz, O. Kenneth, N.H. Lindner, and D.\\n Gershoni, Deterministic generation of a cluster state of entangled photons, Science 354, 434\\n (2016).\\n[10] H. Wang, Y. He, Y.-H. Li, Z.-E. Su, B. Li, H.-L. Huang, X. Ding, M.-C. Chen, C. Liu, J. Qin,\\n J.-P. Li, Y.-M. He, C. Schneider, M. Kamp, C.-Z. Peng, S. Hoefling, C.-Y. Lu, and J.-W. Pan,\\n High-efficiency multiphoton boson sampling, Nature Phot. 11, 361 (2017).\\n[11] C.H.H. Schulte, J. Hansom, A.E. Jones, C. Matthiesen, C. Le Gall, and M. Atature, Quadrature\\n squeezed photons from a two-level system, Nature 525, 222 (2015).\\n[12] D. Huber, M. Reindl, Y. Huo, H. Huang, J.S. Wildmann, O.G. Schmidt, A. Rastelli, and R.\\n Trotta, Highly indistinguishable and strongly entangled photons from symmetric GaAs quantum\\n dots, Nature Comm. 8, 15506 (2017)\\n[13] K. De Greve, P.L. McMahon, D. Press, T.D. Ladd, D. Bisping, C. Schneider, M. Kamp, L.\\n Worschech, S. Hofling, A. Forchel, and Y. Yamamoto, Ultrafast coherent control and suppressed\\n nuclear feedback of a single quantum dot hole qubit, Nature Physics 7, 872 (2011).', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '9e6eac23-ac2f-4095-83e3-51fbd271c644', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 16\\n\\n [14] J.H. Prechtel, A.V. Kuhlmann, J. Houel, A. Ludwig, S.R. Valentin, A.D. Wieck, and R.J.\\n Warburton, Decoupling a hole spin qubit from the nuclear spins, Nature Materials 15, 981\\n (2016)\\n[15] K. Hammerer, A.S. Sørensen, and E.S. Polzik, Quantum interface between light and atomic\\n ensembles, Rev. Mod. Phys. 82, 1041 (2010).\\n [16] C. Santori, D. Fattal, J. Vukovic, G.S. Solomon, and Y. Yamamoto, Indistinguishable photons from\\n a single-photon device, Nature 419, 594 (2002).\\n[17] P. Michler, A. Kiraz, C. Becher, W.V. Schoenfeld, P.M. Petroff, L. Zhang, E. Hu, and A. Imamoglu,\\n A quantum dot single-photon turnstile device, Science 290, 2282 (2000).\\n[18] Y.-M. He, Y. He, Y.-J. Wei, D. Wu, M. Atature, C. Schneider, S. Hofling, M. Kamp, C.-Y. Lu, and\\n J.-W. Pan, On-demand semiconductor single-photon source with near-unity indistinguishability,\\n Nature Nano. 8, 213 (2013).\\n[19] R.J. Warburton, Single spins in self-assembled quantum dots, Nature Materials 12, 483 (2013).\\n [20] J. Bleuse, J. Claudon, M. Creasey, N.S. Malik, J.-M. Gerard, I. Maksymov, J.-P. Hugonin, and\\n P. Lalanne Inhibition, enhancement, and control of spontaneous emission in photonic nanowires\\n Phys. Rev. Lett. 106, 103601 (2011).\\n [21] M. Davanco, M.T. Rakher, W. Wegscheider, D. Schuh, A. Badolato, and K. Srinivasan, Efficient\\n quantum dot single photon extraction into an optical fiber using a nanophotonic directional\\n coupler, Appl. Phys. Lett. 99, 121101 (2011).\\n [22] T.G. Tiecke, K.P. Nayak, J.D. Thompson, T. Peyronel, N.P. de Leon, V. Vuletic, and M.D. Lukin,\\n Efficient fiber-optical interface for nanophotonic devices, Optica 2, 70 (2015).\\n [23] R.S. Daveau, K.C. Balram, T. Pregnolato, J. Liu, E.H. Lee, J.D. Song, V. Verma, R. Mirin, S. Woo\\n Nam, L. Midolo, S. Stobbe, K. Srinivasan, and P. Lodahl, Efficient fiber-coupled single-photon\\n source based on quantum dots in a photonic-crystal waveguide, Optica 4, 178 (2017).\\n [24] A. Kiraz, M. Atature, and A. Imamoglu, Quantum-dot single-photon sources: Prospects for\\n applications in linear optics quantum-information processing, Phys. Rev. A 69, 032305 (2004).\\n [25] P. Tighineanu, C.L. Dreessen, C. Flindt, P. Lodahl, and A.S. Sørensen, Phonon decoherence\\n of quantum dots in photonic structures: Broadening of the zero-phonon line and the role of\\n dimensionality, arXiv:1702.04812 (2017).\\n [26] G. Kirsanske, H. Thyrrestrup, R.S. Daveau, C.L. Dreessen, T. Pregnolato, L. Midolo, P.\\n Tighineanu, A. Javadi, S. Stobbe, R. Schott, A. Ludwig, A.D. Wieck, S.I Park, J.D. Song,\\n A.V. Kuhlmann, I. Sollner, M.C. Lobl, R.J. Warburton, and P. Lodahl, Indistinguishable and\\n efficient single photons from a quantum dot in a planar nanobeam waveguide, arXiv:1701.08131\\n (2017).\\n [27] H. Wang, Z.-C. Duan, Y.-H. Li, S. Chen, J.-P. Li, Y.-M. He, M.-C. Chen, Y. He, X. Ding, C.-Z.\\n Peng, C. Schneider, M. Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, Near-transform-limited\\n single photons from an efficient solid-state quantum emitter, Phys. Rev. Lett. 116, 213601\\n (2016).\\n [28] J.L. O’Brien, A. Furusawa, and J. Vuckovic, Photonic quantum technologies, Nature Photonics 3,\\n 687 (2009).\\n [29] J. Carolan, C. Harrold, C. Sparrow, E. Martin-Lopez, N.J. Russell, J.W. Silverstone, P.J. Shadbolt,\\n N. Matsuda, M. Oguma, M. Itoh, G.D. Marshall, M.G. Thompson, J.C.F. Matthews, T.\\n Hashimoto, J.L. O’Brien, and A. Laing, Universal linear optics, Science 349, 711 (2015).\\n [30] J. Wang, A. Santamato, P. Jiang, D. Bonneau, E. Engin, J.W. Silverstone, M. Lermer, J. Beetz, M.\\n Kamp, S. Hofling, M.G. Tanner, C.M. Natarajan, R.H. Hadfield, S.N. Dorenbos, V. Zwiller, J.L.\\n O’Brien, and M.G. Thompson, Gallium arsenide (GaAs) quantum photonic waveguide circuits,\\n Opt. Commun. 327, 49 (2014).\\n [31] L. Midolo et al., in preparation (2017).\\n [32] L. Midolo, A. Schliesser, and A. Fiore, in preparation (2017).\\n [33] A. Aspuru-Guzik and P. Walther, Photonic quantum simulators Nature Physics 8, 285 (2012).\\n [34] J. Kettler, M. Paul, F. Olbrich, K. Zeuner, M. Jetter, and P. Michler, Single-photon and photon', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '4a9600a2-24a1-4600-9fb2-d10ba3b7c33a', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 17\\n\\n pair emission from MOVPE-grown In(Ga)As quantum dots: shifting the emission wavelength\\n from 1.0 to 1.3 μm Appl. Phys. B 122, 48 (2016).\\n [35] J.-H. Kim, T. Cai, C.J.K. Richardson, R.P. Leavitt, and E. Waks, Two-photon interference from\\n a bright single-photon source at telecom wavelengths, Optica 3, 577 (2016).\\n [36] B. Kambs, J. Kettler, M. Bock, J.N. Becker, C. Arend, A. Lenhard, S.L. Portalupi, M. Jetter,\\n P. Michler, and C. Becher, Low-noise quantum frequency down-conversion of indistinguishable\\n photons, Opt. Express 24, 22250 (2016).\\n [37] R.H. Hadfield, Single-photon detectors for optical quantum information applications, Nature\\n Photonics 3, 696 (2009).\\n[38] G. Reithmaier, S. Lichtmannecker, T. Reichert, P. Hasch, K. Muller, M. Bichler, R. Gross, and J.J.\\n Finley, On-chip time resolved detection of quantum dot emission using integrated superconducting\\n single photon detectors, Sci. Reports 3, 1901 (2013).\\n[39] A. Faraon, I. Fushman, D. Englund, N. Stoltz, P. Petroff, and J. Vukovic, Coherent generation of\\n non-classical light on a chip via photon-induced tunnelling and blockade, Nature Phys. 4, 859\\n (2008).\\n[40] A. Reinhard, T. Volz, M. Winger, A. Badolato, K.J. Hennessy, E.L. Hu, and A. Imamoglu, Strongly\\n correlated photons on a chip, Nature Phot. 6, 93 (2012).\\n[41] M.P. Bakker, T. Ruytenberg, W. Loffler, A. Barve, L. Coldren, M.P. van Exter, and D.\\n Bouwmeester, Quantum dot nonlinearity through cavity-enhanced feedback with a charge memory\\n Phys. Rev. B 91, 241305(R) (2015)\\n[42] A.J. Bennett, J.P. Lee, D.J.P. Ellis, I. Farrer, D.A. Ritchie, and A.J. Shields, A semiconductor\\n photon-sorter Nature Nano. 11, 857 (2016).\\n[43] A. Javadi, I. S¨ollner, M. Arcari, S.L. Hansen, L. Midolo, S. Mahmoodian, G. Kirsanske, T.\\n Pregnolato, E.H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Single-photon non-linear optics\\n with a quantum dot in a waveguide, Nature Comm. 6, 8655 (2015)\\n[44] D. Witthaut, M.D. Lukin, and A.S. Sørensen, Photon sorters and QND detectors using single\\n photon emitters, Europhys. Lett. 97, 50007 (2012).\\n[45] T.C. Ralph, I. S¨ollner, S. Mahmoodian, A.G. White, and P. Lodahl, Photon sorting, efficient Bell\\n measurements and deterministic CZ gate using a passive two-level nonlinearity, Phys. Rev.\\n Lett. 114, 173603 (2015).\\n[46] D.E. Chang, A.S. Sørensen, E.A. Demler, and M.D. Lukin, A single-photon transistor using\\n nanoscale surface plasmons, Nature Phys. 3, 807 (2007).\\n [47] B. Urbaszek, X. Marie, T. Amand, O. Krebs, P. Voisin, P. Maletinsky, A. Hogele, and A. Imamoglu,\\n Nuclear spin physics in quantum dots: An optical investigation, Rev. Mod. Phys. 85, 79 (2013).\\n [48] K. Heshami, C. Santori, B. Khanaliloo, C. Healey, V.M. Acosta, P.E. Barclay, and C. Simon,\\n Raman quantum memory based on an ensemble of nitrogen-vacancy centers coupled to a\\n microcavity, Phys. Rev. A 89, 040301(R) (2014).\\n [49] H.M. Meyer, R. Stockill, M. Steiner, C. Le Gall, C. Matthiesen, E. Clarke, A. Ludwig, J. Reichel,\\n M. Atature, and M. Kohl, Direct photonic coupling of a semiconductor quantum dot and a\\n trapped ion, Phys. Rev. Lett. 114, 123001 (2015).\\n [50] W.B. Gao, P. Fallahi, E. Togan, J. Miguel-Sanchez, and A. Imamoglu, Observation of entanglement\\n between a quantum dot spin and a single photon, Nature 491, 426 (2012).\\n[51] W.B. Gao, P. Fallahi, E. Togan, A. Delteil, Y.S. Chin, J. Miguel-Sanchez, and A. Imamoglu,\\n Quantum teleportation from a propagating photon to a solid-state spin qubit, Nature Comm. 4,\\n 2744 (2013).\\n[52] A. Delteil, Z. Sun, W.B. Gao, E. Togan, S. Faelt, and A. Imamoglu, Generation of heralded\\n entanglement between distant hole spins, Nature Phys. 12, 218 (2016).\\n[53] P. Lodahl, S. Mahmoodian, S. Stobbe, A. Rauschenbeutel, P. Schneeweiss, J. Volz, H. Pichler, and\\n P. Zoller, Chiral quantum optics, Nature 541, 473 (2017).\\n[54] I. S¨ollner, S. Mahmoodian, S. Lindskov Hansen, L. Midolo, A. Javadi, G. Kirsanske, T. Pregnolato,\\n H. El-Ella, E.-H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Deterministic photon-emitter', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '9d6d7ce1-ecb5-4330-ab7f-ae5c4747b61c', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 18\\n coupling in chiral photonic circuits, Nature Nano. 10, 775 A (2015).ˆ\\n [55] R.J. Coles, D.M. Price, J.E. Dixon, B. Royall, E. Clarke, P. Kok, M.S. Skolnick, A.M. Fox,\\n and M.N. Makhonin, Chirality of nanophotonic waveguide with embedded quantum emitter for\\n unidirectional spin transfer, Nature Comm. 7, 11183 (2016).\\n [56] S. Mahmoodian, ˆA P. Lodahl, and ˆA A. S. Sørensen, Quantum networks with chiral light-matter\\n interaction in waveguides, Phys. Rev. Lett. 117, 240501 (2016).\\n [57] P.W. Fry, I.E. Itskevich, D.J. Mowbray, M.S. Skolnick, J.J. Finley, J.A. Barker, E.P. O’Reilly, L.R.\\n Wilson, I.A. Larkin, P.A. Maksym, M. Hopkinson, M. Al-Khafaji, J.P.R. David, A.G. Cullis,\\n G. Hill, and J.C. Clark, Inverted electron-hole alignment in InAs-GaAs self-assembled quantum\\n dots, Phys. Rev. Lett. 84, 733 (2000).\\n [58] F. Ding, R. Singh, J.D. Plumhof, T. Zander, V. Krapek, Y.H. Chen, M. Benyoucef, V. Zwiller, K.\\n Dorr, G. Bester, A. Rastelli, and O.G. Schmidt, Tuning the exciton binding energies in single\\n self-assembled InGaAs/GaAs quantum dots by piezoelectric-induced biaxial stress, Phys. Rev.\\n Lett. 104, 067405 (2010).\\n [59] R. Stockill, M.J. Stanley, L. Huthmacher, E. Clarke, M. Hugues, A.J. Miller, C. Matthiesen, C.\\n Le Gall, and M. Atature, Phase-tuned entangled state generation between distant spin qubits,\\n arXiv:1702.03422 (2017).\\n [60] D. Dzsotjan, A.S. Sørensen, and M. Fleischhauer, Quantum emitters coupled to surface plasmons\\n of a nanowire: A Greens function approach, Phys. Rev. B 82, 075427 (2010).\\n [61] T. Ramos, H. Pichler, A.J. Daley, and P. Zoller, Quantum spin dimers from chiral dissipation in\\n cold-atom chains, Phys. Rev. Lett. 113, 237203 (2014).\\n[62] K.M. Weiss, J.M. Elzerman, Y.L. Delley, J. Miguel-Sanchez, and A. Imamoglu, Coherent two-\\n electron spin qubits in an optically active pair of coupled InGaAs quantum dots, Phys. Rev.\\n Lett. 109, 107401 (2012).\\n[63] T. Rudolph, Why I am optimistic about the silicon-photonic route to quantum computing, APL\\n Photonics 2, 030901 (2017).\\n[64] J.H. Shapiro, Single-photon Kerr nonlinearities do not help quantum computation, Phys. Rev. A\\n 73, 062305 (2006).\\n[65] D. Gottesman and I.L. Chuang, Demonstrating the viability of universal quantum computation\\n using teleportation and single-qubit operations, Nature 402, 390 (1999).\\n[66] L.M. Duan and H.J. Kimble, Scalable photonic quantum computation through cavity-assisted\\n[67] K. Koshino, S. Ishizaka, and Y. Nakamura, Deterministic photon-photon √SWAP gate using a Λinteractions, Phys. Rev. Lett. 92, 127902 (2004).\\n system, Phys. Rev. A 82, 010301(R) (2010).\\n[68] S. Rosenblum, A. Borne, and B. Dayan, Analysis of deterministic swapping of photonic and atomic\\n states through single-photon Raman interaction, Phys. Rev. A 95, 033814 (2017).\\n[69] N.H. Lindner and T. Rudolph, Proposal for pulsed on-demand sources of photonic cluster state\\n strings, Phys. Rev. Lett. 103, 113602 (2009).\\n[70] R. Raussendorf, D.E. Browne, and H.J. Briegel, Measurement-based quantum computation on\\n cluster states, Phys. Rev. A 68, 022312 (2003).\\n[71] S.E. Economou, N. Lindner, and T. Rudolph, Optically generated 2-dimensional photonic cluster\\n state from coupled quantum dots, Phys. Rev. Lett. 105, 093601 (2010).\\n[72] H. Pichler, S. Choi, P. Zoller, and M.D. Lukin, Photonic tensor networks produced by a single\\n quantum emitter, arXiv:1702.02119\\n [73] K. T. K. Azuma and H.-K. Lo, All-photonic quantum repeaters, Nat. Commun. 6, 6787 (2015).\\n [74] D. Buterakos, E. Barnes, and S.E. Economou, Deterministic generation of all-photonic quantum\\n repeaters from solid-state emitters, arXiv:1612.03869 (2016).', 'text_template': '{metadata_str}\\n\\n{content}'}]}, {'documents': [{'class_name': 'Document', 'doc_id': 'be0e8bc0-0012-40bf-90b8-0e50f67a0433', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:quant-ph/0407030v1 5 Jul 2004\\n\\n On quantum state of entangled photon pairs\\n Ruo Peng WANG\\n Department of Physics, Peking University, Beijing 100871, P.R.China\\n Email: rpwang@cis.pku.edu.cn\\n June 18, 2018\\n\\n Abstract\\n I show that the photon pairs used in experimental tests of quantum\\n non-locality based on Bell’s theorem are not in the entangled quantum\\n state. The correct quantum state of the “entangled” photon pairs is sug-\\n gested. Two experiments for testing this quantum state are proposed.\\n PACS: 03.65.Ud, 42.50.Dv\\n Keywords: entangled photon pair, entangled quantum states, quantum\\n non-locality\\n\\n 1 introduction\\n Quantum non-locality is a controversial topic of quantum theory, and entangled\\n photon pairs played a very important role in experimental tests of quantum non-\\n locality. So far, a number of experimental tests of quantum non-locality based\\n on Bell’s theorem [1] have been carried out [2, 3, 4, 5, 6]. In these experiments\\n entangled photon pairs are produced, and the polarization correlation between\\n entangled photon pairs is measured. Experimental results obtained in these tests\\n are in favor for quantum non-locality. Although it has been pointed out that\\n this polarization correlation is compatible with local realism [7], these results\\n are generally considered as direct evidences for the existence of quantum non-\\n locality, and more experiments [8, 9, 10] showing different kind of quantum\\n non-local correlation are carried out. The entangled photon pairs are used as\\n light source in these experiments, and the interpretation of the experimental\\n results as proofs for quantum non-locality is closely related to the assumption\\n that these photon pairs are in entangled quantum state.\\n In this letter, I will show that the same polarization correlation also exists\\n for certain un-entangled photon pairs. And on other hand, based on the consid-\\n eration of momentum conservation, I will also show that the entangled quantum\\n state is not a correct description for photon pairs used in these experimental\\n tests of quantum non-locality. A correct quantum state for these photon pairs is\\n suggested, and two experiments for testing this quantum state and the quantum\\n non-locality are proposed.\\n\\n 1', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '0e51993f-ad65-4849-aaa5-3718b2edf1c8', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '2 polarization correlation between un-entangled\\n photons\\nLet’s consider a beam of un-entangled photon pairs with each photon pair con-\\ntaining a left circle polarized photon and a right circle polarized photon. Such\\na beam of photon pairs can be produced by adjusting polarization state of\\nthe photon pairs obtained from the parametric down conversion with a type-II\\ncollinear phase matching [11]. The quantum state of this un-entangled photon\\npairs can be expressed as\\n |ψ〉 = 2 1(bv †+ ibh†)(bv †− ibh†)|0〉 = 2 1(bv †bv †+ bh†bh†)|0〉, (1)\\nwhere bv† and bh are the creation operator for vertically polarized photon and,†\\nrespectively, for horizontally polarized photon with the wave vector~2. By using k\\nthe Coulomb gauge, we may describe the optical field of these photon pairs by\\nthe vector potential A~. The positive frequencies part of the vector potential is\\nrelated to the photon annihilation operators in the following way\\n A+ = B(bv~v + bh~h)ei~2 ·~\\n ~ e e k r (2)\\nwhere the constant B is given by [12]\\n B =√ μ0ℏc2 (3)\\n 2V ω\\nwith μ0 the magnetic permeability of the vacuum, ω the angular frequency of\\nthe photons.\\n We divide the beam of un-entangled photon pairs into two channels by using\\na half reflecting mirror (BS). The wave vectors of photons in these two channels\\nare~1 and k k ~2. A half wave plate (W P1) is placed into the channel 1 to introduce\\na phase difference of π between the vertical and horizontal components of the\\noptical field, and another half wave plate (W P2) is placed into the channel 2\\nto swap the vertical and horizontal components of the optical field, as shown\\nin FIG. 1. As no incident photons with the wave vector~1 being present, after k\\npassing the beam splitter and the wave plates, the optical field becomes [13]\\n A~ + = √2 (bv~v − bh~h)ei~1 ·~ + √2 (bh~v + bv~h)ei~2 ·~r .Be e k r B e e k (4)\\nThe photons in the channel 1 and 2 are polarized by Glan-Thompsom linear\\npolarization analyzers P1 and P2, respectively, before being detected by single\\nphoton detectors D1 and D2.\\n To analyze the polarization correlation between photons detected by detec-\\ntors D1 and D2, we need expressions of optical field at these detectors. Up to\\na phase factor, we have\\n A+~θ1 = √2 (bv cos θ1 − bh sin θ1)~θ1B e (5)\\n 2', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '99eaa171-c4c6-43bb-8b9c-6deeffc5c6fc', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' 1 D1\\n 2\\n 1(b bh\\n 2 h † †+b bvv † †) 0 1 2 WP1 θ1\\n 1 P1\\n 1 θ2\\n 2 BS 2\\n 1 WP2\\n 2 2 P2\\n 1 D2\\n\\nFigure 1: A schematic illustration of the experimental setup for testing the\\npolarization correlation between un-entangled photon pairs. The polarization\\nstates of optical beams are indicated by arrows.\\n\\nand B\\n A+~θ2 = √2 (bh cos θ2 + bv sin θ2)~θ2 .e (6)\\nIn the above expressions, ~θ1 and ~θ2 are vectors of unit of the transmissione e\\naxes of the analyzer P1, and respectively, P2. The relations (5) and (6) can be\\nrewritten in the following form\\n A+~θ1 = √2 bθ1 ~θ1 , AθB e ~+2 = √2 bθ2~θ2B e (7)\\nwhere\\n bθ1 = bv cos θ1 − bh sin θ1, bθ2 = bh cos θ2 + bv sin θ2 (8)\\nare the annihilation operators for the photon polarized in the direction ~θ1 withe\\nthe wave vector~1 and, respectively, for the photon polarized in the direction k\\n~θ2 with the wave vector~2.\\ne k\\n The coincidence counting rate C(θ1, θ2) is proportional to the probability\\nof annihilating simultaneously one photon polarized in the direction ~θ1 at thee\\ndetector D1 and one photon polarized in the direction ~θ2 at the detector D2.e\\nThis probability is, in its turn, proportional the matrix element\\n 〈ψ|bθ2 b†1 bθ1bθ2|ψ〉 = 〈ψ|(bv † †cos θ1 − bh sin θ1)(bh cos θ2 + bvsin θ2)† † †\\n θ\\n × (bv cos θ1 − bh sin θ1)(bh cos θ2 + bv sin θ2)|ψ〉 (9)\\n = 4 1sin2(θ1 − θ2).\\nTherefore we have C(θ1, θ2) = CM sin2(θ1 − θ2), (10)\\n\\n 3', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'f98c627e-47a9-42c3-83bb-6111f9b06525', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'where CM this the maxim counting rate that occurs at θ1 − θ2 = ±π/2. This\\ncorrelation is just the same as in the case of entangled photon pairs [6, 11], and\\nthe Bell’s inequality is apparently violated also in the above discussed experi-\\nment. The violation of the Bell’s inequality in this case is only apparent, because\\na condition for obtaining the Bell’s inequality is not fulfilled here. Namely, in\\nthe experiment described in this paper, both photons of a photon pair could\\nbe detected in one single channel, with the wave vector k~1 or k~2. Thus, even\\nthough the same polarization correlation as in the case of entangled photon\\npairs is realized in this experiment, no quantum non-locality is involved here.\\nIndeed, after annihilating of a linear polarized photon at the detector D1, the\\nquantum state becomes\\n |ψ′〉 = bθ1 |ψ〉 = 1√2 (cos θ1b† − sin θ1b†)|0〉.v h (11)\\nThe photon in the quantum state |ψ′〉 is not spatially separated from the detector\\nD1 where another photon was annihilated. The probability as the photon in\\nthe quantum state |ψ′〉 being detected at D1 is\\n 〈ψ′|bθ1 bθ1 |ψ′〉 = 2 † 1. (12)\\n3 quantum state of entangled photon pairs\\nNow we arrive at a very important point: the observation of a photon polar-\\nization correlation as expressed in the relation (10) does not form a sufficient\\nevidence for quantum non-locality. Besides this correlation, one must exclude\\nthe possibility of detecting both of the photons in one single channel to ensure\\nthat the phenomena of quantum non-locality are really observed.\\n An apparently non-local correlation as expressed in the relation (10) are ob-\\nserved experimentally for the entangled photon pairs generated in the paramet-\\nric down conversion nonlinear optical processes with a type-II phase matching.\\nWe have to see if the possibility of detecting both of the photons in one single\\nchannel can be ruled out in the experiment so that one can really interpret the\\nexperimental results as an experimental evidence for quantum non-locality. So\\nfar the following quantum state is used to describe this photon pair[11]:\\n |ψe〉 = 1√2 (bv†1b†2 − bvh †2b†1)|0〉,h (13)\\nwhere bh1, b†1, b†2 and bv2 are creation operators for horizontally polarized pho-† †\\n v h\\ntons with the wave vector k~1, for vertically polarized photons with the wave\\nvector~1, for horizontally polarized photons with the wave vector k k ~2, and for\\nvertically polarized photons with the wave vector~2, respectively. By using the k\\ncorresponding photon annihilation operators, we may write the optical field as\\n A+ = B(bv1~v + bh1~h)ei~1 ·~ + B(bv2~v + bh2~h)ei~2 ·~.\\n ~e e e k r e e k r (14)\\n\\n 4', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '288c7d8f-ef47-4768-b601-8119e00d4d55', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'It is easy to verify that there exists a correlation as described by the relation\\n(10) between the polarization of the photon pairs in the quantum state |ψe〉.\\n The expression (13) for the quantum state of the entangled photon pairs was\\nderived by taking into consideration of the energy and the momentum conserva-\\ntion in the parametric down conversion process. The energy and the momentum\\nof a photon pair in the state |ψe〉 is equal to the energy and the momentum of\\nthe incident photon from which the photon pair is generated. But there is\\na problem in this argument, namely the parametric down conversion process\\ntakes place in nonlinear optical crystals, so one must take the photons and the\\ncrystal as one single system when the condition of the energy and the momen-\\ntum conservation is applied. The macroscopic properties of nonlinear optical\\ncrystals do not change in the parametric down conversion process. Thus the\\nexpectation values of the energy and the momentum of crystals are the same\\nbefore and after the photon pairs’ generation. Therefore the expectation val-\\nues of energy and momentum of a photon pair must equal to the expectation\\nvalues of energy and momentum of incident photon. This condition is satisfied\\nby the quantum state |ψe〉. On other hand, photons are coupled with optical\\nphonons with same momentum when passing through crystals, and there exist\\nexchanges of energy and momentum between photons and crystals through the\\nphoton-phonon interaction during photons’ propagation in crystals. One may\\nneglect the energy exchange between photons and crystals because the ener-\\ngies of phonons are much smaller than that of photons with same momentum,\\nbut the momentum exchange between photons and crystals must be taken into\\nconsideration. This exchange of momentum causes fluctuations in momenta of\\ncrystals and generated photon pairs. But the state |ψe〉 is an eigen-state of the\\nphoton pairs’ momentum, that means there is not momentum fluctuations for\\nphoton pairs in the state |ψe〉. Therefore the generated photon pair can not be\\nin this quantum state.\\n According to the calculation made before, the correlation between the po-\\nlarization of the photon pairs as described by the relation (10) does exist also\\nfor photon pairs in the following quantum state\\n |ψu〉 = 2 1(b1 †+ ib2†)(b1 †− ib2†)|0〉 = 2 1(b1†b1 †+ b2†b2†)|0〉, (15)\\nwith the optical field given by\\n A~u += √2 (b1~v − b2~h)ei~1 ·~ + √2 (b2~v + b1~h)ei~2 ·~r .B e e k r B e e k (16)\\nThe quantum state |ψu〉 is an un-entangled state. In fact, we can write |ψu〉 as\\n |ψu〉 = |ψa〉 ⊗ |ψb〉, (17)\\nwith †|0〉, |ψb〉 = bb†|0〉,\\n |ψa〉 = ba (18)\\nwhere †= 1 †= 1\\n ba √2 (b1 †+ ib2†), bb √2 (b1 †− ib2†), [ba†, bb] = 0. (19)\\n 5', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '43b30d97-ad73-4167-b142-fec2d80f5ce9', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' The quantum state |ψu〉 is not an eigen-state of momentum, but photon\\npairs in the quantum states |ψe〉 and |ψu〉 have the same expectation values of\\nthe energy and the momentum. So the quantum state |ψu〉 satisfies the energy\\nand momentum conservation requirement. In conclusion, the quantum state\\n|ψu〉 could be a correct description for photon pairs generated in the paramet-\\nric down conversion nonlinear optical processes with a type-II phase matching.\\nOne may observe that for photon pairs in the quantum state |ψu〉, both photons\\ncould be detected in one single channel. Therefore, by applying the conditions\\nof the energy and momentum conservation, we can not rule out the possibility\\nof detecting both photons in one single channel. But, instead, we find that the\\nphoton pairs can not be in the entangled state. The apparently non-local cor-\\nrelation between the polarization of photon pairs produced in the parametric\\ndown conversion nonlinear optical processes, that were believed being in entan-\\ngled states, is not a proof for the existence of the quantum non-locality, but just\\na necessary evidence for the fact that photon pairs are in the quantum state\\n|ψu〉.\\n The same conclusion holds also for photon pairs emitted in a radiative atomic\\ncascade of calcium [2, 3]. In that process, electrons which emit two photons in a\\nradiative cascade are well confined within the ions of calcium. The uncertainty\\nin the momentum of electrons implies that the photon pairs can not be in the\\nentangled state which has a well defined momentum. The quantum state for\\nphoton pairs generated in this process could be expressed as\\n |ψu′〉 = 1√2 (bωhb†2h+ bω† 1 ω †1v b†2v )|0〉,ω (20)\\nwhere bω1h, b†1v are creation operators for horizontally, and respectively, ver-†ω\\ntically polarized photons of circle frequency ω1, and bω2h, b†2v are the same† ω\\noperators for photons of circle frequency ω2.\\n The optical field in the channel 1 is given by\\n A+= g11(bω1 v~v + bωh~h)eicω1~1 ·~ + g12(bω2v~v + bω2h~h)eicω2~n1·~r\\n ~1 e 1 e n r e e (21)\\nand\\n A+= g21(bω1 v~v + bωh~h)eicω1~1 ·~ + g22(bω2v~v + bω2h~h)eicω2~n·~r\\n ~2 e e n r e e 1 (22)\\n 1\\nin the channel 2, where ~1, ~2 are vectors of unity that indicate the propaga-n n\\ntion directions of the channels 1 and 2, and g11, g12, g21, g22 are coefficients that\\ndepend on the geometry of the experiment setup. We can now calculate the\\nprobability of annihilating simultaneously a photon of circle frequency ω1 po-\\nlarized in the direction ~θ1 in the channel 1 and a photon of circle frequency ω2e\\npolarized in the direction ~θ2 in the channel 2. By using the expressions (20),e\\n(21) and (22), we find\\n C12(θ1, θ2) = C′M cos2(θ1 − θ2). (23)\\nThis is exactly the correlation observed in experimental tests carried out by\\nAspect et al. [2, 3]. Again, no quantum non-locality is evolved, and both of the\\nphotons can be detected in one single channel also in these tests.\\n\\n 6', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '120d303e-23a4-49c2-8a47-0a738f91d646', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' D1\\n θ1\\n 1 P1\\n “entangled”\\n photon pair 2 BS θ4\\n 4\\n P3 3 P4\\n θ3 D4\\n D3\\n\\nFigure 2: A schematic illustration of the experimental setup for testing the\\npossibility of detecting both photons from one “entangled” photon pair in one\\nsingle channel. The polarization states of optical beams are indicated by arrows.\\n\\n4 proposal for experimental tests\\nDirect experimental tests on the possibility of detecting both photons in one\\nsingle channel and on the coherence of photons in different channels could make\\nour conclusions on the quantum state of “entangled” photons pairs and quan-\\ntum non-locality more convincing. An experimental test on the possibility of\\ndetecting both of the photons in one single channel can be done by using an\\nexperimental setup shown in FIG. 2. This setup is quite similar to that used\\nfor tests of quantum non-locality based on Bell’s theorem. But a half reflecting\\nmirror is inserted now into the channel 2 to split it into the channels 3 and 4,\\nand the coincidence counting rate between the channels 3 and 4 is measured.\\nThis coincidence counting rate is proportional to the polarization correlation\\nbetween photons in the channel 3 and in the channel 4. An anti-coincidence\\ncondition with the signal from the channel 1 can also be applied to ensure that\\nthis coincidence counting rate is not from other sources. By using the expres-\\nsions (15), (16), (13) and (14), one can easily verify that the following relation\\nholds for this coincidence counting rate\\n C(θ3, θ4) = CMcos2(θ3 − θ4),′′ (24)\\nif the photon pairs are in the quantum state |ψu〉, and\\n\\n C(θ3, θ4) ≡ 0, (25)\\nif the photon pairs are in the quantum state |ψe〉.\\n The coherence of photons in different channels can also be used for testing\\nthe quantum state of “entangled” photon pairs. An experimental setup for such\\na test is schematically illustrated in FIG. 3. The linear polarizers P1 and P2 are\\n\\n 7', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '7a329a04-32f6-4bd5-911c-1f787517abfb', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' M1\\n P1\\n 1 WP D\\n “entangled”\\n photon pair 2\\n (x,y)\\n P2\\n M2\\n\\nFigure 3: A schematic illustration of the experimental setup for testing the\\ncoherence between photons from one “entangled” photon pair in different chan-\\nnels. The polarization states of optical beams are indicated by arrows.\\n\\ninserted into the beams of “entangled” photon pairs, generated in the parametric\\ndown conversion nonlinear optical processes with a type-II phase matching, in\\nsuch a way, so that the photon in the channel 1 becomes horizontally polarized,\\nwhile the photon in the channel 2 is polarized vertically. The polarization of\\nthe beam 1 is changed to vertical later by the half wave plate W P . Both beams\\nare reflected by the mirrors M1 and M2 to overlap each other. The single\\nphoton counting rate I(x, y) in the (x, y) plan as a function of the coordinates\\n(x, y) is measured by using the single photon detector D. Let f1(x, y)~v bee\\nthe distribution of the vector potential of the beam 1 in the plan (x, y), and\\nf2(x, y)~v the distribution of the vector potential of the beam 2 . If the photone\\npair were in the quantum state |ψe〉, we have\\n A+(x, y) = bh1f1(x, y)~v + bv2f2(x, y)~v .\\n ~ e e (26)\\n The single photon counting rate I(x, y) is proportional to the matrix element\\n I(x, y) ∝ 〈ψe|~+†(x, y) · A+(x, y)|ψe〉. A~ (27)\\nBy using the relation (26) and the expression (13) for |ψe〉, we find, for photon\\npairs in the quantum state |ψe〉,\\n I(x, y) ∝ |f1(x, y)|2 + |f2(x, y)|2, (28)\\nso no interference occurs. But if the photon pairs were in the quantum state\\n|ψu〉, then according to Eq.(16), we have\\n A+(x, y) = b2f1(x, y)~v + b2f2(x, y)~v .\\n ~ e e (29)\\nAnd in this case the counting rate becomes\\n I(x, y) ∝ |f1(x, y) + f2(x, y)|2, (30)\\nthat means the beam 1 and beam 2 prepared in the way described above are\\ncoherent.\\n\\n 8', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '8e86a476-b4c4-4c21-b244-d82f87eca2a4', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '5 discussion\\nI have shown that the correlations of photons’ polarization observed in “en-\\ntangled” photon pairs generated in the parametric down conversion nonlinear\\noptical processes with a type-II phase matching and in a radiative atomic cas-\\ncade of calcium are not proofs for quantum non-locality. Instead, they are\\nnecessary evidences for the fact that “entangled” photon pairs are in the un-\\nentangled states |ψu〉 or |ψu′〉. According to the expression (16) for the vector\\npotential, in the case of “entangled” photon pairs generated in the parametric\\ndown conversion nonlinear optical processes with a type-II phase matching, we† and b2 †in terms of operators bv1, bv† †2, bh1 and bh2:† †\\nmay express the operators b1\\n b1 = 1† √2 (bv1 + bh2), b2 † † †= 1√2 (bv2 − bh1).† † (31)\\nWe have then\\n |ψu〉 = 1√2 |ψe〉 + 4 1(bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉.† v † v † h † h (32)\\nAs the component (bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉 in |ψu〉 has no contri-† † † †\\n v v h h\\nbution to the coincidence counting rate between the signals from the channel 1\\nand the channel 2, all apparently non-local correlations that were believed as\\nspecificity of photon pairs in the entangled state |ψe〉, occur also in the case of\\nun-entangled photon pairs in the state |ψu〉. The same conclusion holds also\\nfor “entangled” photon pairs generated in a radiative atomic cascade of cal-\\ncium Therefore no physical phenomena that necessitate introducing quantum\\nnon-locality for their explanation are really observed.\\n Einstein, Podolsky, Rosen (EPR) and Bohm had put the completeness of\\nquantum mechanics in contradiction to the relativistic causality by supposing\\nthe existence of particle pairs in entangled quantum states [14, 15]. But till\\nnow, such a contradiction did not occur, because no particle pairs in entangled\\nquantum states had been produced. Can particle pairs in entangled quantum\\nstates be generated ever? It is most likely not. Due to the interaction with\\nthe source of particle pairs, it should be impossible for the produced particle\\npair with different momenta to be in a quantum state with well defined mo-\\nmentum, such as the entangled quantum state. This observation is consistent\\nwith Santos’s suggestion [7] that only quantum states which do not contradict\\nwith locality requirement are physical states. From this point of view, the EPR\\nparadox is just a spectacular illustration of the restriction on quantum states\\nimposed by locality requirement.\\n\\nReferences\\n[1] J. S. Bell, Physics 1, 195 (1964)\\n[2] A. Aspect, P. Grangier, and G. Roger, Phys. Rev. Lett. 49, 91 (1981)\\n\\n 9', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '170e2b68-f50d-4a3b-8155-79c7b12a3da1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '[3] A. Aspect, J. Dalibard, and G. Roger, Phys. Rev. Lett. 49, 1804 (1981)\\n[4] Z. Y. Ou, and L. Mandel, Phys. Rev. Lett. 61, 50 (1988)\\n[5] Y. H. Shih, and C. O. Alley, Phys. Rev. Lett. 61, 2921 (1988)\\n[6] Y. H. Shih, A. V. Sergienko, Morton H. Rubin, T. E. Kiess and C. O. Alley,\\n Phys. Rev. A 50, 23 (1994)\\n[7] E. Santos, Phys. Rev. A 46, 3646 (1992)\\n[8] D. V. Strekalov, A. V. Sergienko, D. N. Klyshko and Y. H. Shih, Phys. Rev.\\n Lett. 74, 3600 (1995)\\n[9] E. J. S. Fonseca, P. H. Souto Ribeiro, S. P´adua and C. H. Monken, Phys.\\n Rev. A 60, 1530 (1999)\\n[10] Z. Zhao, et al., Phys. Rev. Lett. 91, 180401 (2003)\\n[11] P. G. Kwiat et al., Phys. Rev. Lett. 75, 4337 (1995)\\n[12] F. Mandl and G. Shaw, Quantum field theory (John Wiley and Sons, Chich-\\n ester, 1984), Chap. 1\\n[13] L. Mandel and E. Wolf, Optical Coherence and Quantum Optics (Cam-\\n bridge University Press, 1995), p. 640\\n[14] A. Einstein, B. Pdolsky, and N. Rosen, Phys. Rev. 47, 777 (1935)\\n[15] D. Bohm, Quantum Theory (Prentice Hall, Englewood Cliffs, NJ, 1951)\\n\\n 10', 'text_template': '{metadata_str}\\n\\n{content}'}]}, {'documents': []}]\n", + "--------------------------------------------------\n", + "Transition number: 3\n", + "Transition type: finish_branch\n", + "Transition output: {'documents': [{'class_name': 'Document', 'doc_id': '278bd226-32be-4488-a970-323f579ee8e1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:1707.02094v1 [quant-ph] 7 Jul 2017\\n\\n Quantum-dot based photonic quantum networks\\n Peter Lodahl\\n Niels Bohr Institute, University of Copenhagen, Blegdamsvej 17, DK-2100\\n Copenhagen, Denmark\\n E-mail: lodahl@nbi.ku.dk\\n\\n Abstract. Quantum dots embedded in photonic nanostructures have in recent years\\n proven to be a very powerful solid-state platform for quantum optics experiments. The\\n combination of near-unity radiative coupling of a single quantum dot to a photonic\\n mode and the ability to eliminate decoherence processes imply that an unprecedent\\n light-matter interface can be obtained. As a result, high-cooperativity photon-\\n emitter quantum interfaces can be constructed opening a path-way to deterministic\\n photonic quantum gates for quantum-information processing applications. In the\\n present manuscript, I review current state-of-the-art on quantum dot devices and their\\n applications for quantum technology. The overarching long-term goal of the research\\n field is to construct photonic quantum networks where remote entanglement can be\\n distributed over long distances by photons.\\n\\n 1. Introduction\\n\\n The ultimate vision of photonic quantum technology is to construct a complex quantum\\n network of stationary quantum nodes connected by flying photons in a fully quantum\\n way, i.e. quantum entanglement may become distributed. Such a new photonic\\n paradigm would have novel applications within secure quantum communication and\\n is proposed as a way of scaling up quantum computers. It is popularly referred to as the\\n ’quantum internet’ [1] and has been rooted in the atomic physics community where an\\n impressive proof-of-concept elementary two-node quantum network has been achieved\\n [2]. Solid-state alternatives to atomic single-photon emitters are attractive, since unlike\\n atoms they do not require complex laser cooling and trapping techniques. On the\\n other hand solid-state systems are often considered to be ’noisy’ in the sense that many\\n potential decoherence processes may deteriorate quantum properties. Remarkably, self-\\n assembled quantum dots (QDs) emitting single photons in the optical domain, notably\\n InGaAs QDs embedded in GaAs semiconductors, have matured dramatically within\\n the last few years. By systematically studying and combating the relevant decoherence\\n processes [3] impressive coherence has been demonstrated including near-perfect single-\\n photon indistinguishability (above 98%) of two subsequently emitted photons [4, 5] and\\n transform-limited emission lines [6]. Combined with the ability to dramatically enhance\\n light-matter interaction in photonic nanostructures [7], this enables near-deterministic', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'c6fdc95e-eabb-4b5e-a3c0-4c315d4c8f59', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 2\\n\\nsingle-photon sources with an internal collection efficiency exceeding 98 % [8]. This\\nmajor progress entails that QD single-photon sources are now gradually outperforming\\nthe traditional approaches based on atoms or spontaneous parametric down-conversion.\\nQD sources benefit from fast operation speeds, excellent stability and brightness, as well\\nas the potential scalability to multiple single photons and emitters. Notably, a number of\\nimpressive experiments have recently been implemented with QDs that have not been\\naccomplished on other platforms. They include the experimental demonstration of a\\ndeterministic entangled cluster state of strings of photons [9], boson sampling with so far\\nfive photons [10], the detection of squeezed light correlations in resonance fluorescence\\n[11], and the demonstration of an on-demand entangled photon source with higher\\nthan 90% fidelity [12]. Time is now to build on these and other achievements in order\\nto scale QD photonic quantum technology and construct large and complex quantum\\narchitectures for quantum-information processing applications.\\n The present manuscript reviews the current state-of-the-art on quantum photonics\\nbased on QD photon emitters towards the overarching goal of constructing photonic\\nquantum networks. At present, a number of basic functionalities have been\\nsuccessfully demonstrated with generally impressive performance. These may constitute\\nfundamental building blocks of photonic quantum networks. In that sense a current\\nmajor challenge for theoretical quantum physicist is to develop resource efficient\\narchitectures tailored to the specific quantum hardware available, i.e., addressing how\\nthe basic ’puzzle pieces’ can be combined. Figure 1 illustrates the ’puzzle pieces’ already\\nbeing developed for the QD platform, and that will be considered here. They include\\nsingle-photon sources, single-photon nonlinearity, photonic circuitry, efficient-coupling\\nto optical fibers, on-chip single-photon detectors, and multi-emitter coupling.\\n QDs enable two different types of quantum resources: they may be employed\\nas a source of single photons or alternatively a single spin trapped in a QD may be\\nmanipulated as a qubit. Figure 2 illustrates these two approaches. In the former case a\\nsingle electron-hole pair is created in a QD by either optical excitation or by controllably\\ntunneling carriers into the QD with the application of an electric field. The electron-\\nhole pair subsequently recombines whereby a single photon is emitted by spontaneous\\nemission within a lifetime of typically 1 ns for a QD in a bulk medium. In the latter\\napproach a single carrier (electron or hole) is prepared in the QD by tunneling. The\\ncarrier spin is coupled to light since a photon may subsequently be absorbed by the QD\\ncreating a negatively (positively) charged exciton for the case of an initial electron (hole).\\nDepending on the operational condition, the spin may have a coherence time of up to\\n1 μs (for holes) [13, 14], which means that in principle many quantum operations can\\nbe carried out with fast optical control techniques before decoherence sets in. For some\\nquantum-information applications, however, longer spin coherence times are required,\\ne.g., in matter based quantum-repeater architectures. To this end, the QD platform\\nmust be interfaced with long-lived quantum memories based on, e.g., defect centers\\nin diamond or atomic ensembles [15]. Such hybrid quantum network architectures are\\noutside the scope of the present Review.', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a19b0d3b-ebad-4d36-a615-6a4ee63a39ad', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 3\\n ~l ~\\n Nonlinearity Processing\\n wn\\n Fiber Out-coupling Single photons Detection Dipole-dipole interaction\\nFigure 1: Illustration of basic functionalities that can be implemented with QDs and\\nphotonic nanostructures. Left panel: efficient outcoupling taper sections from photonic\\nnanostructures to optical fibers can be engineered to obtain highly efficient outcoupling\\nof single photons. Center panel: a single QD in nanophotonic waveguide or cavity can be\\nused as a highly efficient and coherent source of single photons. Two sources are shown\\nin order to illustrate potential scalability of the approach. Right panel: a single QD\\nefficiently coupled to a waveguide may be employed as a single-photon nonlinearity\\n(upper left), complex photonic circuits may be constructed on a photonic chip for\\nquantum processing of photons (upper right), single-photon superconducting detectors\\nmay be implemented on-chip for highly efficient detection (lower right), and multiple\\nQDs may be coupled by engineering the dipole-dipole interaction in a nanophotonic\\nwaveguide. The figure is a courtesy of Sahand Mahmoodian.\\n 2 2\\n Ia) hole k(b)\\n electron M MM\\nFigure 2: Photon and spin qubits generated with QDs. The light grey areas illustrate\\nthe regions of InAs embedded in dark grey GaAs regions thereby creating an energy\\nwell potential for both electrons and holes. (a) An electron-hole pair trapped in the\\nQD may recombine by emitting a single photon. (b) A single electron (or hole) trapped\\nin the QD constitutes a spin qubit (orange arrow) that may be manipulated by optical\\nmethods.', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '54241ad6-863a-4e89-b9c2-25ea50cede61', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 4\\n\\n2. Single-photon sources\\n\\nA QD efficiently coupled to a nanophotonic cavity or waveguide offers a promising\\nroute towards a deterministic single-photon source. Early pioneering work showed that\\nPurcell enhancement in an optical cavity may be employed to both improve brightness\\nand coherence of the source [16]. Ideally a useful single-photon source emits an optical\\npulse containing a single photon in a useful optical mode (e.g., into an optical fiber)\\nevery time the QD is triggered by either an optical or electrical pulse. Furthermore,\\nmost applications in quantum-information processing require that the emitted photons\\nare fully coherent, which entails that decoherence processes occurring on a time scale\\nof the radiative decay of the QD must be eliminated. Thus, three major figures-of-\\nmerit need to be optimized: i) the single-photon purity, ii) the single-photon coupling\\nefficiency, and iii) the indistinguishability of the emitted photons.\\n Re i): The discrete level structure of self-assembled QDs having widely separated\\noptical transitions implies that they are capable of emitting high-purity single photons\\n[17]. This is gauged in a Hanbury Brown - Twiss (HBT) correlation experiment by\\nrecording the multi-photon emission probability. By using samples with a low QD\\ndensity and implementing (quasi)-resonant excitation to selectively excite only a single\\nQD, excellent single-photon purity can be obtained at the level of g(2)(0) ≤ 0.1%, cf.\\nFig. 3.\\n Re ii): The overall coupling efficiency determines the brightness of the single-photon\\nsource and is comprised of several factors [7]: the excitation and single-photon emission\\nprobability of the QD, the efficiency with which the emitted photons are channeled to a\\nsingle mode (the β-factor), and the transfer efficiency to a low-loss propagating mode,\\ne.g., an optical fiber. Significant progress has been reported on all three engineering\\ntasks: resonant π-pulse excitation allows deterministically preparing a single exciton in\\na QD [18] and electrically gated structures eliminate blinking between different exciton\\ncomplexes [19]. Embedding QDs in photonic nanostructures allows reaching a near-\\nunity β-factor, which has been obtained in nanophotonic waveguides [8, 20] and cavities\\n[4]. Finally, a variety of approaches and designs can be implemented for transferring\\nthe collected photons to an optical fiber using, e.g., tailored gratings for coupling light\\nguided on planar structures vertically off the chip, or waveguide taper sections designed\\nto couple directly to a fiber either by direct or evanescent coupling [21, 22, 23].\\n Re iii): The indistinguishability of an emitted photon is determined by the amount\\nof decoherence taking place within the emitter decay time. With a typical decay time\\nof nanoseconds, the primary decoherence source is coupling of the QD to phonons\\nand potentially photon jitter induced by relaxation processes originating from non-\\nresonant excitation schemes [24]. The latter may be overcome by strict resonant\\nexcitation. Phonon interactions at the contrary are unavoidable and lead to two\\neffects: wide phonon sidebands and residual broadening of the zero-phonon line. The\\nformer contribution (containing typically about 10 % of the the total emission at\\nlow temperatures [7]) can readily be filtered away either by an external filter thereby', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'de71718d-112b-478e-b755-5c7d40825e52', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 5\\n\\nreducing the overall efficiency or more favorably by implementing narrow-band Purcell\\nenhancement. Hence the broadening of the zero-phonon line remains the fundamental\\ndecoherence mechanism. An in-depth theoretical study of this process including the\\nrole of the dimensionality of the photonic nanostructure can be found in Ref. [25].\\nImportantly a clear route to indistinguishable photons with near-unity visibility can be\\nlaid out, which has also been confirmed experimentally [4, 5, 26].\\n The previous discussion illustrates that QDs are capable of producing a truly on-\\ndemand source of single photons by implementing, in a single device, the functionalities\\ndemonstrated so far only in different experiments. It is likely that such a source will\\nbe developed experimentally soon. In particular, it is remarkable that decoherence\\nprocesses can be overcome in a solid-state system to such an extent that near-unity\\nindistinguishability between subsequently emitted photons from the same QD can be\\nobtained. Next step is to convert a single QD source generating photons as ”pearls\\non a string” into a de-multiplexed source delivering many identical single photons\\nsimultaneously in individual modes. Such a source can be constructed by cascading\\nelectro-optical switches and coupling the photons into different optical fibers in order\\nto overcome the time delay between subsequently emitted photons, cf. Fig. 4(a).\\nTo this end, the demonstration of near-transform-limited QD emission lines [6] has\\nthe remarkable consequence that a single QD can emit thousands of indistinguishable\\nphotons thereby providing a huge quantum resource. Indeed recent demonstration of\\nindistinguishability exceeding 90 % between two photons separated by more than 1000\\nemission events has proven this explicitly [27].\\n\\n3. Integrated quantum photonics\\n\\nAs detailed in the previous section, currently the most reliable approach to generate\\nmultiple single photons utilizes a single integrated QD source where the photons\\nare coupled off-chip to an optical fiber, and highly-efficient bulk optical components\\nimplemented for switching and routing, cf. Fig. 4(a). Nonetheless, the potential benefits\\nof integrating functionalities on-chip are many in terms of stability, ease of operation,\\nlow loss, and speed [28]. Figure 4(b) shows an architecture for an integrated de-\\nmultiplexed source of single photons based on a single QD in a planar nanophotonic\\nwaveguide platform. To implement this scheme requires the development of high-\\nefficiency and fast switches, and multi-port waveguide-fiber interfaces. The former\\nrequires the development of new devices and functionalities on-chip where so-far the\\nmost common approach for reconfigurable circuitry has been the application of thermo-\\noptic phase shifters [29]. De-multiplexing requires fast switches, and two promising\\napproaches include electro-optical modulation implemented directly in the host material\\nof the QD (i.e. GaAs) [30, 31] or electro-mechanical coupling [32].\\n The crucial parameter determining the size of the photonic resource that can be\\ngenerated with a de-multiplexed single-photon source is the overall efficiency, including\\nswitching, propagation, and coupling efficiencies. Consequently the rate of N-photon', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '5982b6b2-feb7-48e0-9d25-1a0c89baaf44', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 6\\n 15 pairs taper WG\\n ~Fiber\\n pairs\\n Tum\\n Wch\\n HBT\\n 3 0.8 = Channel\\n 1 WG\\n Fiber taperWG\\n 0.4-\\n 0.2 300 r 140 nm\\n \"37.5 25.0 -12.5 Delay (ns) 12.50,0 25,0 37.5 a\\n 1 0.2 Az\\n 3 060.81.0 HOMpol:\\n 1 0.8 inputNanobeam waveguidePC 90.10 samplecryostat 0.8\\n 0.6 0.6\\n 0.0 0.4 0.4 ]\\n ] 1.00.8\\n 0.6 NU pOlvIpot 1 0.2 Photonic-crystal waveguide 0.2\\n 0.4\\n 0.2 1 0.60.4\\n 0.2 0.4\\n 0.6\\n 0.2\\n Time delay (ns) 850 900 950 1000\\n Wavelength (nm)\\nFigure 3: Purity, indistinguishability, and out-coupling efficiency of QD single-\\nphoton sources. Left column: Examples of HBT and Hong-Ou-Mandel (HOM)\\nindistinguishability measurements by implementing resonant excitation on a QD in\\na micropillar cavity (upper figure shows the cavity structure). g(2)(0) = 0.009 and\\na single-photon indistinguishability of 96.4% is extracted from the two sets of data.\\nFigures reproduced from Ref. [5]. Right column: Examples of devices made for out-\\ncoupling single photon from a QD in a planar nanophotonic waveguide to an optical\\nfiber with high efficiency by tailored evanescent coupling. The upper panel is reproduced\\nfrom Ref. [21]. Lower panel: application of this method to couple out single photons\\nfrom high β-factor nanophotonic waveguides, i.e., nanobeam waveguides and photonic-\\ncrystal waveguides. By recording the reflection and transmission spectrum of the device,\\na chip-to-fiber coupling efficiency of > 80% is obtained. Data reproduced from Ref. [23].\\n\\ngeneration is RN = Rpump ×ηN /N , where η is the overall transmission efficiency from the\\nsource to the fiber and Rpump is the repetition rate of the excitation laser. Exemplarily', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '22f52865-e757-49a9-8244-cad99bf66572', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 7\\n PC (b) Electrically-controlled\\n switches\\n de-muitipieett QD 8-98% Array of delay fibers\\n 2\\n Oei\\n Photonic crystal\\n ultte ~fow-Ioss deleco e waveguide\\n photonic circuit\\nFigure 4: Illustrations of schemes for de-multiplexing a single-photon source. (a)\\na five-photon source obtained by cascading four Pockels cells (PCs) and polarizing\\nbeamsplitters to couple subsequently emitted photons from the QD into different optical\\nfibers with different delay lengths. This de-multiplexed source is applied for proof-\\nof-concept boson sampling. Figure reproduced from Ref. [10]. (b) Architecture\\nof an integrated de-multiplexed source based on a QD coupled with high efficiency\\nto a photonic-crystal waveguide. The emitted photons are transferred to dielectric\\nwaveguides and subsequently routed by electrical switches and coupled off-chip to\\ndifferent fiber delays in order to compensate for the time delay in-between photons.\\nThe figure is a courtesy of Leonardo Midolo.\\n\\nconsidering η = 50% and Rpump = 80 MHz (corresponding to the repetition rate of a\\nTi:Sapph. laser) corresponds to a 10-photon generation rate of R10 ∼ 8 kHz, which\\nillustrates the promising prospects of this approach while posing clear benchmarks for\\nthe efficiency of the applied switching and coupling technology.\\n Integrating a de-multiplexed source on a chip is one immediate goal. Another\\nimportant task is to develop complex integrated tunable photonic circuits that can\\nprocess the generated single photons leading to applications for quantum simulations\\n[33]. Current state-of-the-art of this technology is a six-mode reprogrammable circuit\\ncapable of implementing universal high-fidelity quantum gates [29]. Considerable effort\\nis directed to the scaling of these circuits in working towards a fully integrated system\\nwhere photon source and processing circuit would be implemented on a single photonic\\nchip. However, implementing on-chip the relatively long optical delays (typically tens of\\nnanoseconds) required to interfere subsequently emitted photons, poses a challenge for\\nthis approach in requiring slow-light optical buffers. Consequently at present it seems\\nmost realistic to consider approaches based on two separate chips, connected by optical\\nfibers, where one chip constitutes the source and the other chip the quantum processor.\\n The operation wavelength of a quantum photonics processor is an essential\\nparameter. The preferred operation wavelength is within the telecom C-band (1.55 μm),\\nwhere advanced high-performance optical components and fiber technology developed\\nfor telecom industry are present. The natural emission wavelength of self-assembled high\\nquality QDs is around 950 nm and while significant efforts are taken to develop QDs in\\nthe telecom bands as well [34, 35] the excellent performance in terms of coherence and\\nefficiency required for quantum technology has not yet been achieved. An alternative', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '6005404e-7547-45e3-b282-52c7be36abcf', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 8\\n\\napproach employs frequency down-conversion of single photons from the near-infrared\\nto the telecom band. Noise free conversion of single photons from a QD with an\\nefficiency exceeding 30% has been demonstrated [36], which could be further increased by\\nimproving the in- and out-coupling efficiency through the nonlinear conversion crystal.\\nThe two-chip approach alluded to above has the asset that the frequency conversion\\nstep could naturally be incorporated in between the source chip and the processing\\nchip. Furthermore, implementing frequency conversion of QD photons also has another\\nadvantage, since it provides a way of overcoming spectral inhomogeneities of photons\\nemitted by different QDs by transducing them to the same wavelength in the C-band\\nwith the implementation of a tunable pump laser. This could prove a powerful way of\\nscaling up QD systems to couple multiple emitter qubits (e.g., spins) in addition to the\\nmultiple photon qubits already considered.\\n Finally, another essential requirement is the ability to detect optical photons with\\nvery high efficiency. Very significant progress has been obtained in recent years with the\\ndevelopment of superconducting nanowire single-photon detectors [37], where the single-\\nphoton detection efficiency today approaches 100% and the speed is compatible with QD\\nsources. Importantly these detectors can naturally be integrated on the planar GaAs\\nplatform containing QDs [38], which means that photon source, circuit, and detection\\ncould potentially be integrated on a single chip, which may pave the way to ultimately\\nlow-loss photonic quantum nodes.\\n\\n4. Single-photon nonlinearity\\n\\nThe previous sections concerned the generation of single photons and their subsequent\\nquantum interference in photonic circuits. Many quantum-information applications\\nof photons require generating photon-photon interactions. Generally photons interact\\nweakly meaning that standard nonlinearities are typically too weak to be operational\\nat the level of single photons. However, the efficient coupling of single QDs to photonic\\nnanostructures imply that such a system may be exploited to mediate a giant nonlinear\\nresponse operational at the level of single photons. Two different approaches can be\\ntaken to reach a giant nonlinearity with a QD or any other quantum emitter: the QD\\ncan be strongly coupled to a cavity and the nonlinearity of the Jaynes-Cummings ladder\\nexploited [39, 40], or a QD very efficiently coupled to a single optical mode in a cavity\\n[41, 42] or waveguide [43] may be used as a saturable nonlinearity. The latter approach\\nis beneficial for ease of operation, since it is less sensitive to the exact tuning of the\\nwavelength of the QD. Furthermore, the saturable QD nonlinearity generally can be\\noperated at a weaker incident photon flux [43]. The key operational principle of the QD\\nnonlinearity is as follows: a narrow-band pulse containing a single photon is reflected\\nfrom a QD in a waveguide due to destructive interference of forward scattering and the\\nincoming field. The efficient coupling (high β-factor) and coherent interaction, which\\ncan be achieved with QDs, implies that the interaction between the photon and the\\nQD is deterministic. Since a two-level emitter can only scatter one photon at a time,', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'c1b95287-4dfa-411d-8269-8450b9dac1db', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 9\\n\\ntwo photons have an increased probability to be transmitted past the QD leading to a\\nphoton sorting process. From an experimental point of view this type of nonlinearity\\nis attractive since no active control over the quantum state of the QD is required,\\ni.e., the QD is merely exploited as a passive scatterer. On the other hand, the photon\\nsorting is not ideal even for a perfect coupling efficiency. This implies that while a single-\\nphoton component of a pulse may be deterministically reflected, two- and higher-photon\\ncomponents are only partially transmitted and in this process entanglement is generated\\nbetween the two photons [44]. One approach of overcoming this limitation and obtain\\ndeterministic photon sorting has been suggested by combining the QD nonlinearity with\\na nonlinear spatio-temporal mode selector [45]. Such an approach may be applied for\\nconstructing a resource-efficient Bell-state analyzer [44, 45], cf. Section 7. It should be\\nmentioned that having a three-level emitter with coherent control of the level populations\\nin the waveguide opens a range of additional opportunities. Such level schemes can be\\nimplemented with charged QDs, cf. the description in the next section. One exciting\\nproposal is that of a photonic transistor controlled by only a single photon [46].\\n\\n5. Spin-photon interfaces\\n\\nIntroducing a single spin in a QD leads to a range of additional opportunities. It grants\\naccess to two separate ground states (spin up or down) that can be exploited as a\\nquantum memory to encode a qubit. Either single holes or electrons can be employed\\nand may be deterministically prepared by controlling the tunnel barriers in electrically\\ncontacted semiconductor structures. Various level structures can be implemented by the\\nuse of Zeeman tuning with a magnetic field, and spin initialization, manipulation, and\\nread-out can be achieved rapidly with short optical pulses (typically in the nanosecond\\nrange), cf. Ref. [19] for a detailed review of the physics of single spins in QDs.\\nThe spin lifetime can be in the range of milliseconds or longer in a large magnetic\\nfield, which is determined by phonon-mediated spin relaxation processes. However,\\nin quantum applications the coherence time of the spin is the essential parameter.\\nA coherence time of up to T 2∗ > 460 ns has been reported for hole spins that has\\nthe advantage compared to electrons that they do not suffer from the Fermi contact\\nhyperfine interaction with nuclear spins [14]. Importantly the spin coherence time can\\nbe several orders of magnitude longer than the spin manipulation time, which means\\nthat multiple quantum gate operations could be implemented within the spin coherence\\ntime. Unravelling spin decoherence mechanisms in QDs is a research topic in itself and is\\noutside the scope of the present review. For a thorough discussion, see Refs. [19, 47]. It\\nis anticipated that further research efforts will enable increasing the spin coherence time\\nfurther. Nonetheless, it seems unlikely that it will be possible to extend the coherence\\ntime by many orders of magnitude in present QD systems. In particular, long-range\\nquantum repeater protocols would require millisecond range quantum memories. This\\nentails a hybrid approach where the single photons generated by QDs are interfaced\\nwith long-lived quantum memories, e.g., vacancy centers in diamond [48], ions [49], or', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '093afd39-07a0-4589-a0c9-eb679ce77c2f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 10\\n\\n potentially mechanical systems.\\n Considering a charged exciton in an external magnetic field, various four-level\\n emitter schemes can be engineered with two ground states corresponding to either\\n (pseudo)spin up or down of the electron (hole). The recombination of a charged exciton\\n leads to the emission of polarized photons, where the polarization of the photon is\\n correlated with the resulting spin state. In this way entanglement between the QD\\n spin and the polarization of the emitted photon can be generated [13, 50] where\\n in general hyper-entanglement between spin, polarization, and frequency is obtained.\\n Subsequently the information in one of the variables can be erased to create a Bell state.\\n This has been the resource enabling quantum teleportation between a single photon and\\n a solid-state spin [51]. A further extension of this experiment was the demonstration of\\n heralded entanglement between two spatially separate hole spins in QDs [52], cf. Fig. 5\\n for the experimental layout.\\n Another possibility is to couple QD spins directly to photon path by exploiting\\n the concept of chiral light-matter interaction [53]. By tailoring the local electric field\\n in a nanophotonic waveguide to match the in-plane circular dipole moment of one of\\n the charged exciton transitions, it is possible to induce deterministic directional photon\\n emission [54], i.e. σ± transitions emit in different directions. This effect was used\\nto demonstrate an interface between QD spin and the photon path [55]. Based on\\nthis functionality a photonic quantum network architecture was put forward and its\\nfeasibility evaluated for QDs in nanophotonic waveguides [56], cf. Fig. 5.\\n\\n6. Coupling of multiple quantum dots\\n\\nWe have seen in previous sections how a single QD deterministically coupled to a single\\noptical mode may be exploited as a source of multiple photonic qubits potentially\\ngenerating a large quantum resource. It is natural to consider the option of scaling\\nfurther up by coupling multiple QDs. This is generally a demanding task, as it is\\nthe case for any solid-state optical emitter, since inhomogeneous broadening of a QD\\nensemble (typically at the level of THz, which should be compared to the sub-GHz\\nhomogenous linewidth of a QD) implies that significant spectral mismatch would need\\nto be overcome. Various tuning methods of QDs have been implemented including\\nelectric-field tuning [57] and strain tuning [58] that needs to be implemented locally to\\neach individual QD. Eventually not only the central emission frequency of the different\\nemitters must be controlled, but the complete photon pulses need to be matched in\\norder to faithfully couple multiple QDs. An important figure-of-merit of the success is\\ngauged in a quantum-interference experiment between two separate QD single-photon\\nsources, where an interference visibility of 93% has been reported in a weak-excitation\\nregime [59], which is a highly encouragin result for this approach.\\n Given the ability to radiatively couple two or more QDs, a photonic nanostructure\\ncan be constructed to enhance and tailor the coupling. Multiple QDs coupled by\\ndipole-dipole interaction lead to the formation of collective quantum states that can', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '39ce6fe7-fe46-40b9-9d03-35a978fc7363', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 11\\n Entanglement and\\n non-local measurement\\n Spin preparation and Spin preparation and local (a) Chiral element\\n local measurements (QDI) Ti Sapphiremeasurements (QD2)\\n laser\\n Diode las 0 +\\n Diode lasers /1)\\n EOM (b) Network node\\n EOMs EOMs\\n Intranode parity measurement Internode entanglement\\n BSI Detector 0 Detector\\n Emitter 0\\n Fiber Emitter\\n Single photon Communication photon\\n source\\n N Gratings BS2\\n (c) Linked Nodes\\n filters\\n Single-photon detectors Photon routing\\n QDI OD2\\n 5 m\\nFigure 5: Left panel: experimental setup employed for the generation of remote\\nentanglement between two QD hole spins in two different cryostats separated by\\n5 m. Figure reproduced from Ref. [52]. Right panel: Architecture of a photonic\\nquantum network based on chiral photon-emitter elements (a) embedded in on-chip\\ninterferometers constituting fundamental quantum nodes (b). The quantum nodes are\\nlinked together by photons for remote entanglement distribution (c). Figure reproduced\\nfrom Ref. [56].\\n\\nhave sub- or super-radiant character. In photonic nanostructures, the dipole-dipole\\ninteraction can be significantly extended beyond the behavior found in a homogenous\\n(3D) medium, where the coupling falls off rapidly with distance d (scales as d−3). In\\na 1D waveguide with radiative coupling to the waveguide as the dominating process,\\ndipole-dipole interaction may be infinitely extended, i.e. in practice it is limited by\\nthe absorption or scattering loss of the guided mode. Controlling the distance between\\nmultiple emitters enables the switching between sub- and super-radiant behavior. Apart\\nfrom the fundamental interest, the long-range dipole-dipole coupling may be exploited\\nto construct quantum gates between stationary qubits [60]. Introducing chiral coupling\\nopens new opportunities by the ability to engineer the directional coupling between\\nemitters. This may lead to novel opportunities for quantum simulators and for entering\\nnew regimes of coupled photons and emitters. For instance directional coupling has been\\nfound to lead to the formation of quantum dimers in the steady state of the system [61].\\nAnother approach to scaling to multiple emitters exploits tunnel coupling between QDs\\ngrown in close mutual vicinity. With such an approach advanced level structures can\\nbe engineered in these QD molecules where it has been demonstrated that the spin\\ncoherence time can be significantly extended [62].', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '5516db24-13d0-408f-a09f-df9415a927b9', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 12\\n\\n7. Quantum network architectures\\n\\nWe have reviewed various basic quantum functionalities and hardware that can be\\nimplemented with the use of QDs embedded in modern nanophotonic structures. It\\nis a pertinent task, to consider what applications and actual architectures can be\\nimplemented with these quantum resources and how to make them most resource\\nefficient. This section briefly highlights some of the opportunities and future directions.\\nA general observation, as discussed in detail above, is that QD systems can generate\\nmany single-photon qubits, while the scaling up to many matter qubits is expected to\\nbe much more challenging. It is important to have these general guidelines in mind\\nwhen designing resource efficient quantum network architectures that is tailored to the\\nquantum hardware based on QDs.\\n As discussed in the present Review, QDs in nanophotonic structures are very well\\nsuited for generating highly coherent single photons on demand at a high repetition\\nrate. Such a source can generate thousands of indistinguishable photons [27] that if\\nefficiently routed could be an important photonic resource for quantum simulations [33].\\nFurthermore, recent developments on how to efficiently fuse photonic qubits with linear\\noptics have the renewed excitement on how to construct single-photon based quantum\\ncomputing [63].\\n Introducing a single-photon nonlinearity to the toolbox opens for a range of\\nadditional possibilities. The most simple case is that of a two-level emitter, and here\\ndistortion of the optical pulses associated with photon-emitter bound state contributions\\nhas been interpreted as essentially a ”no-go theorem” for quantum computation [64].\\nOne work around, however, has been identified where the pulse distortion is actually\\nexploited such that one- and two-photon pulses are scattered into orthogonal spectral-\\ntemporal modes that can subsequently be separated, e.g., by nonlinear frequency\\nconversion [45]. With such a functionality all path-encoded photonic Bell states can\\nbe deterministically measured exploiting only four non-linear interaction process, linear\\noptics, and photon detection, cf. Fig. 6. Based on a Bell-state analyzer, quantum\\ncomputing can in principle be implemented by teleportation based gates and linear\\noptical quantum computing [65]. Alternatively, a photonic CZ gate can be directly\\nbased on the two-level emitter nonlinearity. However, in this case the pulse distortion\\nintroduced by photon scattering has to be undone by scattering an additional time on\\nthe emitter. However, since this scattering process is not time symmetric the photon\\npulse would need to be reverted in time in between the two scattering process. Such a\\ntime reversal of the photon pulses could in principle be obtained by storing, reverting,\\nand releasing photon pulses in a gradient-echo quantum memory.\\n Having access to a 3-level Λ-scheme with two stable ground states leads to many\\nmore opportunities since now the emitter can store a qubit for extended times. Coherent\\ncontrol of the qubit state enables implementing a Duan-Kimble CNOT gate for photons\\nwhere two subsequently interacting photons are entangled by their successive interaction\\nwith the same emitter [66]. Another related approach employs single-photon Raman', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'd1065b19-dd0a-4177-9589-f30e9ba2ae04', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 13\\n 1 photon\\n TLS SFG components\\n 2 photon\\n components\\n classical dichroic\\n pump splitter\\n Q1\\n Q2\\nFigure 6: Illustration of a deterministic Bell-state analyzer based on single-photon\\nnonlinearity, frequency conversion, and linear optics. Upper plot: a deterministic two-\\nlevel scattering (TLS) process induced, e.g., in a nanophotonic waveguide may be\\noperated such that one and two photons are scattered to orthogonal spatio-temporal\\nmodes. The two modes can subsequently be selected by implementing active filtering,\\ne.g. nonlinear sum-frequency generation (SFG) where the spatio-temporal mode of the\\nclassical pump is matched to, e.g., the one-photon component of the scattered light.\\nConsequently, only the single-photon component is frequency converted and can be\\nseparated from the two-photon component by a dichroic splitter. Lower plot: optical\\ncircuit containing four photon-sorting elements that is capable of determining all four\\npath-encoded Bell states by photon coincidence detection. Figure reproduced from Ref.\\n[45].\\n\\ninteraction to deterministically swap the quantum state of the emitter and a photon\\n[67, 68]. A full architecture of a quantum network based on QDs chirally coupled to\\nnanophotonic waveguides has been put forward in Ref. [56], cf. Fig. 5. Here the very\\nlarge cooperativity obtainable with QDs leads to a gate fidelity approaching unity for\\nexperimentally realistic parameters.\\n Another highly exciting direction is to exploit a single QD efficiently coupled to a\\nnanophotonic waveguide to directly produce entangled photons. To this end, Lindner', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a5cf3e02-ee6b-47ea-9b4d-e330002f40f5', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 14\\n\\nand Rudolph have put forward the proposal of a photonic ”cluster state machine gun”\\nbased on a QD that could potentially producing long strings of photons in an entangled\\ncluster state [69]. In this proposal, a single QD spin is brought into a superposition\\nstate of up and down and optically excited to a superposition of charged exciton states.\\nSubsequently the QD decays by spontaneous emission of a single photon thereby creating\\nan entangled state between the polarization of the emitted photon and the spin state.\\nThe excitation-emission process can be repeated multiple times in which case a large\\nGHZ photonic state is generated. If π/2 rotations on the QD spin are carried out\\nin-between photon emissions, an entangled photonic cluster state is created. A QD\\nembedded in a nanophotonic waveguide could potentially serve as a source of large 1D\\ncluster states, where the cluster size is determined by the number of emission events\\n(determined by the QD lifetime that is typically ∼ 100 ps in a nanophotonic structure)\\nthat can take place within the coherence time of the spin (up to ∼ μs). A pioneering\\nexperiment of photonic cluster state generation with a QD source has recently been\\nreported [9], which validates the approach.\\n Cluster states have attracted a lot of attention since it was realized that they enable\\nuniversal quantum computing. The general philosophy behind such one-way quantum\\ncomputing architectures [70] is that a large scale entangled state is generated ”up front”\\nand computation carried out by single-qubit operations induced by measurements.\\nImportantly, quantum computing requires access to 2D photonic cluster states as\\nopposed to the 1D cluster states considered above. Several proposals of how to\\nachieve this with QDs have been put forward. One approach is to optically couple\\ntwo QDs [71], which requires local tuning of the two QDs into mutual resonance. An\\nalternative proposal has been put forward based on a single quantum emitter, where the\\nemitted photons are controllably coupled back to interact with the emitter and through\\nthis process generates the 2D cluster [72]. Such a ”one-emitter quantum-computer\\narchitecture” is almost ideally suited for implementation based on QDs in nanophotonic\\nstructures. Another exciting potential applications of photonic cluster states would be in\\nquantum repeater protocols without the necessity of a matter-based quantum memory\\n[73, 74]. Such an approach would potentially overcome the main bottleneck of the QD\\nbased platform for quantum networks resulting from the relatively short-lived quantum\\nmemory offered by the QD spin.\\n\\n8. Conclusions\\n\\nQDs in photonic nanostructures constitute an unprecedent photon-emitter interface at\\noptical frequencies. The recent significant progress implies that advanced quantum-\\ninformation processing protocols now become within experimental reach in the overall\\nquest towards constructing photonic quantum networks. Such a distributed photonic\\nquantum network would open new avenues for secure quantum communication and\\nultimately provide a way of scaling up quantum computers. It is currently an exciting\\nand timely research topic to identify how the emerging quantum hardware can most', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '2ba64cf8-ce27-4486-8bc8-6dc319c814d7', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'Quantum-dot based photonic quantum networks 15\\n\\nefficiently be ’put to use’ by laying out realistic and resource efficient quantum network\\narchitectures. Such a research programme requests a close interplay between theory and\\nexperiment in a continuous effort on how to exploit new quantum technology.\\n\\n9. Acknowledgements\\n\\nIt is a pleasure to acknowledge all the excellent colleagues who have contributed to my\\nunderstanding of solid-state photonic quantum networks including A.S. Sørensen, R.J.\\nWarburton, T.C. Ralph, and A.G. White, as well as all past and present members of\\nthe Quantum Photonics Group at the Niels Bohr Institute, University of Copenhagen.\\nI thank Nir Rotenberg for proof reading the manuscript. I gratefully acknowledge\\nfinancial support from the European Research Council (ERC Advanced Grant SCALE ),\\nInnovation Fund Denmark (Quantum Innovation Center Qubiz ), and the Danish Council\\nfor Independent Research.\\n [1] H. J. Kimble, The quantum internet, Nature 453, 1023 (2008).\\n [2] A. Reiserer and G. Rempe, Cavity-based quantum networks with single atoms and optical photons,\\n Rev. Mod. Phys. 87, 1379 (2015).\\n [3] A.V. Kuhlmann, J. Houel, A. Ludwig, L. Greuter, D. Reuter, A.D. Wieck, M. Poggio, and R.J.\\n Warburton, Charge noise and spin noise in a semiconductor quantum device, Nature Phys. 9,\\n 570 (2013).\\n [4] N. Somaschi, V. Giesz, L. De Santis, J. C. Loredo, M. P. Almeida, G. Hornecker, S. L. Portalupi,\\n T. Grange, C. Anton, J. Demory, C. Gomez, I. Sagnes, N. D. Lanzillotti-Kimura, A. Lema ˜Atre,\\n A. Auffeves, A. G. White, L. Lanco, and P. Senellart, Near-optimal single-photon sources in the\\n solid state, Nature Phot. 10, 340 (2016).\\n [5] X. Ding, Y. He, Z.-C. Duan, N. Gregersen, M.-C. Chen, S. Unsleber, S. Maier, C. Schneider, M.\\n Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, On-demand single photons with high extraction\\n efficiency and near-unity indistinguishability from a resonantly driven quantum dot in a\\n micropillar, Phys. Rev. Lett. 116, 020401 (2016).\\n [6] A.V. Kuhlmann, J.H. Prechtel, J. Houel, A. Ludwig, D. Reuter, A.D. Wieck, and R.J. Warburton,\\n Transform-limited single photons from a single quantum dot, Nature Comm. 6, 8204 (2015)\\n [7] P. Lodahl, S. Mahmoodian, and S. Stobbe, Interfacing single photons and single quantum dots\\n with photonic nanostructures, Rev. Mod. Phys. 87, 347 (2015).\\n [8] M. Arcari, I. S¨ollner, A. Javadi, S. Lindskov Hansen, S. Mahmoodian, J. Liu, H. Thyrrestrup,\\n E. H. Lee, J. D. Song, S. Stobbe, and P. Lodahl, Near-unity coupling efficiency of a quantum\\n emitter to a photonic crystal waveguide, Phys. Rev. Lett. 113, 093603 (2014).\\n [9] I. Schwartz, D. Cogan, E.R. Schmidgall, Y. Don, L. Gantz, O. Kenneth, N.H. Lindner, and D.\\n Gershoni, Deterministic generation of a cluster state of entangled photons, Science 354, 434\\n (2016).\\n[10] H. Wang, Y. He, Y.-H. Li, Z.-E. Su, B. Li, H.-L. Huang, X. Ding, M.-C. Chen, C. Liu, J. Qin,\\n J.-P. Li, Y.-M. He, C. Schneider, M. Kamp, C.-Z. Peng, S. Hoefling, C.-Y. Lu, and J.-W. Pan,\\n High-efficiency multiphoton boson sampling, Nature Phot. 11, 361 (2017).\\n[11] C.H.H. Schulte, J. Hansom, A.E. Jones, C. Matthiesen, C. Le Gall, and M. Atature, Quadrature\\n squeezed photons from a two-level system, Nature 525, 222 (2015).\\n[12] D. Huber, M. Reindl, Y. Huo, H. Huang, J.S. Wildmann, O.G. Schmidt, A. Rastelli, and R.\\n Trotta, Highly indistinguishable and strongly entangled photons from symmetric GaAs quantum\\n dots, Nature Comm. 8, 15506 (2017)\\n[13] K. De Greve, P.L. McMahon, D. Press, T.D. Ladd, D. Bisping, C. Schneider, M. Kamp, L.\\n Worschech, S. Hofling, A. Forchel, and Y. Yamamoto, Ultrafast coherent control and suppressed\\n nuclear feedback of a single quantum dot hole qubit, Nature Physics 7, 872 (2011).', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '9e6eac23-ac2f-4095-83e3-51fbd271c644', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 16\\n\\n [14] J.H. Prechtel, A.V. Kuhlmann, J. Houel, A. Ludwig, S.R. Valentin, A.D. Wieck, and R.J.\\n Warburton, Decoupling a hole spin qubit from the nuclear spins, Nature Materials 15, 981\\n (2016)\\n[15] K. Hammerer, A.S. Sørensen, and E.S. Polzik, Quantum interface between light and atomic\\n ensembles, Rev. Mod. Phys. 82, 1041 (2010).\\n [16] C. Santori, D. Fattal, J. Vukovic, G.S. Solomon, and Y. Yamamoto, Indistinguishable photons from\\n a single-photon device, Nature 419, 594 (2002).\\n[17] P. Michler, A. Kiraz, C. Becher, W.V. Schoenfeld, P.M. Petroff, L. Zhang, E. Hu, and A. Imamoglu,\\n A quantum dot single-photon turnstile device, Science 290, 2282 (2000).\\n[18] Y.-M. He, Y. He, Y.-J. Wei, D. Wu, M. Atature, C. Schneider, S. Hofling, M. Kamp, C.-Y. Lu, and\\n J.-W. Pan, On-demand semiconductor single-photon source with near-unity indistinguishability,\\n Nature Nano. 8, 213 (2013).\\n[19] R.J. Warburton, Single spins in self-assembled quantum dots, Nature Materials 12, 483 (2013).\\n [20] J. Bleuse, J. Claudon, M. Creasey, N.S. Malik, J.-M. Gerard, I. Maksymov, J.-P. Hugonin, and\\n P. Lalanne Inhibition, enhancement, and control of spontaneous emission in photonic nanowires\\n Phys. Rev. Lett. 106, 103601 (2011).\\n [21] M. Davanco, M.T. Rakher, W. Wegscheider, D. Schuh, A. Badolato, and K. Srinivasan, Efficient\\n quantum dot single photon extraction into an optical fiber using a nanophotonic directional\\n coupler, Appl. Phys. Lett. 99, 121101 (2011).\\n [22] T.G. Tiecke, K.P. Nayak, J.D. Thompson, T. Peyronel, N.P. de Leon, V. Vuletic, and M.D. Lukin,\\n Efficient fiber-optical interface for nanophotonic devices, Optica 2, 70 (2015).\\n [23] R.S. Daveau, K.C. Balram, T. Pregnolato, J. Liu, E.H. Lee, J.D. Song, V. Verma, R. Mirin, S. Woo\\n Nam, L. Midolo, S. Stobbe, K. Srinivasan, and P. Lodahl, Efficient fiber-coupled single-photon\\n source based on quantum dots in a photonic-crystal waveguide, Optica 4, 178 (2017).\\n [24] A. Kiraz, M. Atature, and A. Imamoglu, Quantum-dot single-photon sources: Prospects for\\n applications in linear optics quantum-information processing, Phys. Rev. A 69, 032305 (2004).\\n [25] P. Tighineanu, C.L. Dreessen, C. Flindt, P. Lodahl, and A.S. Sørensen, Phonon decoherence\\n of quantum dots in photonic structures: Broadening of the zero-phonon line and the role of\\n dimensionality, arXiv:1702.04812 (2017).\\n [26] G. Kirsanske, H. Thyrrestrup, R.S. Daveau, C.L. Dreessen, T. Pregnolato, L. Midolo, P.\\n Tighineanu, A. Javadi, S. Stobbe, R. Schott, A. Ludwig, A.D. Wieck, S.I Park, J.D. Song,\\n A.V. Kuhlmann, I. Sollner, M.C. Lobl, R.J. Warburton, and P. Lodahl, Indistinguishable and\\n efficient single photons from a quantum dot in a planar nanobeam waveguide, arXiv:1701.08131\\n (2017).\\n [27] H. Wang, Z.-C. Duan, Y.-H. Li, S. Chen, J.-P. Li, Y.-M. He, M.-C. Chen, Y. He, X. Ding, C.-Z.\\n Peng, C. Schneider, M. Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, Near-transform-limited\\n single photons from an efficient solid-state quantum emitter, Phys. Rev. Lett. 116, 213601\\n (2016).\\n [28] J.L. O’Brien, A. Furusawa, and J. Vuckovic, Photonic quantum technologies, Nature Photonics 3,\\n 687 (2009).\\n [29] J. Carolan, C. Harrold, C. Sparrow, E. Martin-Lopez, N.J. Russell, J.W. Silverstone, P.J. Shadbolt,\\n N. Matsuda, M. Oguma, M. Itoh, G.D. Marshall, M.G. Thompson, J.C.F. Matthews, T.\\n Hashimoto, J.L. O’Brien, and A. Laing, Universal linear optics, Science 349, 711 (2015).\\n [30] J. Wang, A. Santamato, P. Jiang, D. Bonneau, E. Engin, J.W. Silverstone, M. Lermer, J. Beetz, M.\\n Kamp, S. Hofling, M.G. Tanner, C.M. Natarajan, R.H. Hadfield, S.N. Dorenbos, V. Zwiller, J.L.\\n O’Brien, and M.G. Thompson, Gallium arsenide (GaAs) quantum photonic waveguide circuits,\\n Opt. Commun. 327, 49 (2014).\\n [31] L. Midolo et al., in preparation (2017).\\n [32] L. Midolo, A. Schliesser, and A. Fiore, in preparation (2017).\\n [33] A. Aspuru-Guzik and P. Walther, Photonic quantum simulators Nature Physics 8, 285 (2012).\\n [34] J. Kettler, M. Paul, F. Olbrich, K. Zeuner, M. Jetter, and P. Michler, Single-photon and photon', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '4a9600a2-24a1-4600-9fb2-d10ba3b7c33a', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 17\\n\\n pair emission from MOVPE-grown In(Ga)As quantum dots: shifting the emission wavelength\\n from 1.0 to 1.3 μm Appl. Phys. B 122, 48 (2016).\\n [35] J.-H. Kim, T. Cai, C.J.K. Richardson, R.P. Leavitt, and E. Waks, Two-photon interference from\\n a bright single-photon source at telecom wavelengths, Optica 3, 577 (2016).\\n [36] B. Kambs, J. Kettler, M. Bock, J.N. Becker, C. Arend, A. Lenhard, S.L. Portalupi, M. Jetter,\\n P. Michler, and C. Becher, Low-noise quantum frequency down-conversion of indistinguishable\\n photons, Opt. Express 24, 22250 (2016).\\n [37] R.H. Hadfield, Single-photon detectors for optical quantum information applications, Nature\\n Photonics 3, 696 (2009).\\n[38] G. Reithmaier, S. Lichtmannecker, T. Reichert, P. Hasch, K. Muller, M. Bichler, R. Gross, and J.J.\\n Finley, On-chip time resolved detection of quantum dot emission using integrated superconducting\\n single photon detectors, Sci. Reports 3, 1901 (2013).\\n[39] A. Faraon, I. Fushman, D. Englund, N. Stoltz, P. Petroff, and J. Vukovic, Coherent generation of\\n non-classical light on a chip via photon-induced tunnelling and blockade, Nature Phys. 4, 859\\n (2008).\\n[40] A. Reinhard, T. Volz, M. Winger, A. Badolato, K.J. Hennessy, E.L. Hu, and A. Imamoglu, Strongly\\n correlated photons on a chip, Nature Phot. 6, 93 (2012).\\n[41] M.P. Bakker, T. Ruytenberg, W. Loffler, A. Barve, L. Coldren, M.P. van Exter, and D.\\n Bouwmeester, Quantum dot nonlinearity through cavity-enhanced feedback with a charge memory\\n Phys. Rev. B 91, 241305(R) (2015)\\n[42] A.J. Bennett, J.P. Lee, D.J.P. Ellis, I. Farrer, D.A. Ritchie, and A.J. Shields, A semiconductor\\n photon-sorter Nature Nano. 11, 857 (2016).\\n[43] A. Javadi, I. S¨ollner, M. Arcari, S.L. Hansen, L. Midolo, S. Mahmoodian, G. Kirsanske, T.\\n Pregnolato, E.H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Single-photon non-linear optics\\n with a quantum dot in a waveguide, Nature Comm. 6, 8655 (2015)\\n[44] D. Witthaut, M.D. Lukin, and A.S. Sørensen, Photon sorters and QND detectors using single\\n photon emitters, Europhys. Lett. 97, 50007 (2012).\\n[45] T.C. Ralph, I. S¨ollner, S. Mahmoodian, A.G. White, and P. Lodahl, Photon sorting, efficient Bell\\n measurements and deterministic CZ gate using a passive two-level nonlinearity, Phys. Rev.\\n Lett. 114, 173603 (2015).\\n[46] D.E. Chang, A.S. Sørensen, E.A. Demler, and M.D. Lukin, A single-photon transistor using\\n nanoscale surface plasmons, Nature Phys. 3, 807 (2007).\\n [47] B. Urbaszek, X. Marie, T. Amand, O. Krebs, P. Voisin, P. Maletinsky, A. Hogele, and A. Imamoglu,\\n Nuclear spin physics in quantum dots: An optical investigation, Rev. Mod. Phys. 85, 79 (2013).\\n [48] K. Heshami, C. Santori, B. Khanaliloo, C. Healey, V.M. Acosta, P.E. Barclay, and C. Simon,\\n Raman quantum memory based on an ensemble of nitrogen-vacancy centers coupled to a\\n microcavity, Phys. Rev. A 89, 040301(R) (2014).\\n [49] H.M. Meyer, R. Stockill, M. Steiner, C. Le Gall, C. Matthiesen, E. Clarke, A. Ludwig, J. Reichel,\\n M. Atature, and M. Kohl, Direct photonic coupling of a semiconductor quantum dot and a\\n trapped ion, Phys. Rev. Lett. 114, 123001 (2015).\\n [50] W.B. Gao, P. Fallahi, E. Togan, J. Miguel-Sanchez, and A. Imamoglu, Observation of entanglement\\n between a quantum dot spin and a single photon, Nature 491, 426 (2012).\\n[51] W.B. Gao, P. Fallahi, E. Togan, A. Delteil, Y.S. Chin, J. Miguel-Sanchez, and A. Imamoglu,\\n Quantum teleportation from a propagating photon to a solid-state spin qubit, Nature Comm. 4,\\n 2744 (2013).\\n[52] A. Delteil, Z. Sun, W.B. Gao, E. Togan, S. Faelt, and A. Imamoglu, Generation of heralded\\n entanglement between distant hole spins, Nature Phys. 12, 218 (2016).\\n[53] P. Lodahl, S. Mahmoodian, S. Stobbe, A. Rauschenbeutel, P. Schneeweiss, J. Volz, H. Pichler, and\\n P. Zoller, Chiral quantum optics, Nature 541, 473 (2017).\\n[54] I. S¨ollner, S. Mahmoodian, S. Lindskov Hansen, L. Midolo, A. Javadi, G. Kirsanske, T. Pregnolato,\\n H. El-Ella, E.-H. Lee, J.D. Song, S. Stobbe, and P. Lodahl, Deterministic photon-emitter', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '9d6d7ce1-ecb5-4330-ab7f-ae5c4747b61c', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum-dot based photonic quantum networks'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' Quantum-dot based photonic quantum networks 18\\n coupling in chiral photonic circuits, Nature Nano. 10, 775 A (2015).ˆ\\n [55] R.J. Coles, D.M. Price, J.E. Dixon, B. Royall, E. Clarke, P. Kok, M.S. Skolnick, A.M. Fox,\\n and M.N. Makhonin, Chirality of nanophotonic waveguide with embedded quantum emitter for\\n unidirectional spin transfer, Nature Comm. 7, 11183 (2016).\\n [56] S. Mahmoodian, ˆA P. Lodahl, and ˆA A. S. Sørensen, Quantum networks with chiral light-matter\\n interaction in waveguides, Phys. Rev. Lett. 117, 240501 (2016).\\n [57] P.W. Fry, I.E. Itskevich, D.J. Mowbray, M.S. Skolnick, J.J. Finley, J.A. Barker, E.P. O’Reilly, L.R.\\n Wilson, I.A. Larkin, P.A. Maksym, M. Hopkinson, M. Al-Khafaji, J.P.R. David, A.G. Cullis,\\n G. Hill, and J.C. Clark, Inverted electron-hole alignment in InAs-GaAs self-assembled quantum\\n dots, Phys. Rev. Lett. 84, 733 (2000).\\n [58] F. Ding, R. Singh, J.D. Plumhof, T. Zander, V. Krapek, Y.H. Chen, M. Benyoucef, V. Zwiller, K.\\n Dorr, G. Bester, A. Rastelli, and O.G. Schmidt, Tuning the exciton binding energies in single\\n self-assembled InGaAs/GaAs quantum dots by piezoelectric-induced biaxial stress, Phys. Rev.\\n Lett. 104, 067405 (2010).\\n [59] R. Stockill, M.J. Stanley, L. Huthmacher, E. Clarke, M. Hugues, A.J. Miller, C. Matthiesen, C.\\n Le Gall, and M. Atature, Phase-tuned entangled state generation between distant spin qubits,\\n arXiv:1702.03422 (2017).\\n [60] D. Dzsotjan, A.S. Sørensen, and M. Fleischhauer, Quantum emitters coupled to surface plasmons\\n of a nanowire: A Greens function approach, Phys. Rev. B 82, 075427 (2010).\\n [61] T. Ramos, H. Pichler, A.J. Daley, and P. Zoller, Quantum spin dimers from chiral dissipation in\\n cold-atom chains, Phys. Rev. Lett. 113, 237203 (2014).\\n[62] K.M. Weiss, J.M. Elzerman, Y.L. Delley, J. Miguel-Sanchez, and A. Imamoglu, Coherent two-\\n electron spin qubits in an optically active pair of coupled InGaAs quantum dots, Phys. Rev.\\n Lett. 109, 107401 (2012).\\n[63] T. Rudolph, Why I am optimistic about the silicon-photonic route to quantum computing, APL\\n Photonics 2, 030901 (2017).\\n[64] J.H. Shapiro, Single-photon Kerr nonlinearities do not help quantum computation, Phys. Rev. A\\n 73, 062305 (2006).\\n[65] D. Gottesman and I.L. Chuang, Demonstrating the viability of universal quantum computation\\n using teleportation and single-qubit operations, Nature 402, 390 (1999).\\n[66] L.M. Duan and H.J. Kimble, Scalable photonic quantum computation through cavity-assisted\\n[67] K. Koshino, S. Ishizaka, and Y. Nakamura, Deterministic photon-photon √SWAP gate using a Λinteractions, Phys. Rev. Lett. 92, 127902 (2004).\\n system, Phys. Rev. A 82, 010301(R) (2010).\\n[68] S. Rosenblum, A. Borne, and B. Dayan, Analysis of deterministic swapping of photonic and atomic\\n states through single-photon Raman interaction, Phys. Rev. A 95, 033814 (2017).\\n[69] N.H. Lindner and T. Rudolph, Proposal for pulsed on-demand sources of photonic cluster state\\n strings, Phys. Rev. Lett. 103, 113602 (2009).\\n[70] R. Raussendorf, D.E. Browne, and H.J. Briegel, Measurement-based quantum computation on\\n cluster states, Phys. Rev. A 68, 022312 (2003).\\n[71] S.E. Economou, N. Lindner, and T. Rudolph, Optically generated 2-dimensional photonic cluster\\n state from coupled quantum dots, Phys. Rev. Lett. 105, 093601 (2010).\\n[72] H. Pichler, S. Choi, P. Zoller, and M.D. Lukin, Photonic tensor networks produced by a single\\n quantum emitter, arXiv:1702.02119\\n [73] K. T. K. Azuma and H.-K. Lo, All-photonic quantum repeaters, Nat. Commun. 6, 6787 (2015).\\n [74] D. Buterakos, E. Barnes, and S.E. Economou, Deterministic generation of all-photonic quantum\\n repeaters from solid-state emitters, arXiv:1612.03869 (2016).', 'text_template': '{metadata_str}\\n\\n{content}'}]}\n", + "--------------------------------------------------\n", + "Transition number: 4\n", + "Transition type: finish_branch\n", + "Transition output: {'documents': [{'class_name': 'Document', 'doc_id': '5dde80d8-9fea-4e79-a8c6-4f56f6989bc5', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:quant-ph/0003151v1 31 Mar 2000\\n\\n Unconventional Quantum Computing Devices\\n\\n Seth Lloyd\\n Mechanical Engineering\\n MIT 3-160\\n Cambridge, Mass. 02139\\n\\n Abstract: This paper investigates a variety of unconventional quantum computation de-\\n vices, including fermionic quantum computers and computers that exploit nonlinear quan-\\n tum mechanics. It is shown that unconventional quantum computing devices can in prin-\\n ciple compute some quantities more rapidly than ‘conventional’ quantum computers.\\n\\n Computers are physical: what they can and cannot do is determined by the laws\\n of physics. When scientific progress augments or revises those laws, our picture of what\\n computers can do changes. Currently, quantum mechanics is generally accepted as the\\n fundamental dynamical theory of how physical systems behave. Quantum computers can\\n in principle exploit quantum coherence to perform computational tasks that classical com-\\n puters cannot [1-21]. If someday quantum mechanics should turn out to be incomplete\\n or faulty, then our picture of what computers can do will change. In addition, the set\\n of known quantum phenomena is constantly increasing: essentially any coherent quantum\\n phenomenon involving nonlinear interactions between quantum degrees of freedom can\\n in principle be exploited to perform quantum logic. This paper discusses how the revi-\\n sion of fundamental laws and the discovery of new quantum phenomena can lead to new\\n technologies and algorithms for quantum computers.\\n Since new quantum effects are discovered seemingly every day, let’s first discuss two\\n basic tests that a phenomenon must pass to be able to function as a basis for quantum\\n computation. These are 1) The phenomenon must be nonlinear, and 2) It must be coherent.\\n To support quantum logic, the phenomenon must involve some form of nonlinearity, e.g.,\\n a nonlinear interaction between quantum degrees of freedom. Without such a nonlinearity\\n quantum devices, like linear classical devices, cannot perform even so simple a nonlinear\\n operation as an AND gate. Quantum coherence is a prerequisite for performing tasks\\n such as factoring using Shor’s algorithm [10], quantum simulation a la Feynman [11] and\\n Lloyd [12], or Grover’s data-base search algorithm [13], all of which require extended\\n manipulations of coherent quantum superpositions.\\n\\n 1', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '066b2185-7165-468b-84f7-b8866f9baa4f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' The requirements of nonlinearity and coherence are not only necessary for a phe-\\nnomenon to support quantum computation, they are also in principle sufficient. As shown\\nin [14-15], essentially any nonlinear interaction between quantum degrees of freedom suf-\\nfices to construct universal quantum logic gates that can be assembled into a quantum\\ncomputer. In addition, the work of Preskill et al. [18] on robust quantum computation\\nshows that an error rate of no more than 10−4 per quantum logic operation allows one to\\nperform arbitrarily long quantum computations in principle.\\n In practice, of course, few if any quantum phenomena are likely to prove sufficiently\\ncontrollable to provide extended quantum computation. Promising devices under current\\nexperimental investigation include ion traps [5,7], high finesse cavities for manipulating\\nlight and atoms using quantum electrodynamics [6], and molecular systems that can be\\nmade to compute using nuclear magnetic resonance [8-9]. Such devices store quantum\\ninformation on the states of quantum systems such as photons, atoms, or nuclei, and\\naccomplish quantum logic by manipulating the interactions between the systems via the\\napplication of semiclassical potentials such as microwave or laser fields. We will call such\\ndevices ‘conventional’ quantum computers, if only because such devices have actually been\\nconstructed.\\n There is another sense in which such computers are conventional: although the de-\\nvices described above have already been used to explore new regimes in physics and to\\ncreate and investigate the properties of new and exotic quantum states of matter, they\\nfunction according to well established and well understood laws of physics. Perhaps the\\nmost striking examples of the ‘conventionality’ of current quantum logic devices are NMR\\nquantum microprocessors that are operated using techniques that have been refined for\\nalmost half a century. Ion-trap and quantum electrodynamic quantum computers, though\\ncertainly cutting edge devices, operate in a quantum electrodynamic regime where the\\nfundamental physics has been understood for decades (that is not to say that new and\\nunexpected physics does not arise frequently in this regime, rather that there is general\\nagreement on how to model the dynamics of such devices).\\n Make no mistake about it: a conventional quantum logic device is the best kind of\\nquantum logic device to have around. It is exactly because the physics of nuclear magnetic\\nresonance and quantum electrodynamics are well understood that devices based on this\\nphysics can be used systematically to construct and manipulate the exotic quantum states\\nthat form the basis for quantum computation. With that recognition, let us turn to\\n\\n 2', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'bcf5f805-d98f-4076-aa8c-f753cf7dbfa1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '‘unconventional’ quantum computers.\\n Perhaps the most obvious basis for an unconventional quantum computer is the use\\nof particles with non-Boltzmann statistics in a refime where these statistics play a key role\\nin the dynamics of the device. For example, Lloyd [16] has proposed the use of fermions\\nas the fundamental carriers of quantum information, so that a site or state occupied by a\\nfermion represents a 1 and an unoccupied site or state represents a 0. It is straightforward\\nto design a universal quantum computer using a conditional hopping dynamics on an array\\nof sites, in which a fermion hops from one site to another if only if other sites are occupied.\\n If the array is one-dimensional, then such a fermionic quantum computer is equivalent\\nto a conventional quantum computer via the well-known technique of bosonization. If the\\narray is two or more dimensional, however, a local operation involving fermions on the\\nlattice cannot be mocked up by a local operation on a conventional quantum computer,\\nwhich must explicitly keep track of the phases induced by Fermi statistics. As a result,\\nsuch a fermionic computer can perform certain operations more rapidly than a conventional\\nquantum computer. An obvious example of a problem that can be solved more rapidly on\\na fermionic quantum computer is the problem of simulating a lattice fermionic system in\\ntwo or more dimensions. To get the antisymmetrization right in second quantized form,\\na conventional ‘Boltzmann’ quantum computer takes time proportional to T ℓd−1 where T\\nis the time over which the simulation is to take place, ℓ is the length of the lattice and\\nd is the dimension, while a fermionic quantum computer takes time proportional to T .\\n(Here we assume that the computations for both conventional and Fermionic quantum\\ncomputers can take advantage of the intrinsic parallelizability of such simulations: if the\\ncomputations are performed serially an additional factro of ℓd is required for both types\\nof computer to update each site sequentially.)\\n As the lattice size ℓ and the dimension d grow large, the difference between the two\\ntypes of computer also grows large. Indeed, the problem of simulating fermions hopping\\non a hypercube of dimension d as d → ∞ is evidently exponentially harder on a con-\\nventional quantum computer than a Fermionic quantum computer. Since a variety of\\ndifficult problems such as the travelling-salesman problem and data-base search problem\\ncan be mapped to particles hopping on a hypercube, it is interesting to speculate whether\\nfermionic computers might provide an exponential speed-up on problems of interest in ad-\\ndition to quantum simulation. No such problems are currently known, however. Fermionic\\ncomputers could be realized in principle by manipulating the ways in which electrons and\\n\\n 3', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '53057fa4-ba5b-423b-a686-13c6953d5f12', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'holes hop from site to site on a semiconductor lattice (though problems of decoherence are\\nlikely to be relatively severe for such systems).\\n It might also be possible to construct bosonic computers using photons, phonons, or\\natoms in a Bose-Einstein condensate. Such systems can be highly coherent and support\\nnonlinear interactions: phonons and photons can interact in a nonlinear fshion via their\\ncommon nonlinear interaction with matter, and atoms in a Bose condensate can be made\\nto interact bia quantum electrodynamics (by introduction of a cavity) or by collisions. So\\nfar, however, the feature of Bose condensates that makes them so interesting from the point\\nof view of physics — all particles in the same state — makes them less interesting from the\\npoint of view of quantum computation. Many particles in the same state, which can be\\nmanipulated coherently by a variety of techniques, explore the same volume of Hilbert space\\nas a single particle in that state. As a result, it is unclear how such a bosonic system could\\nprovide a speed-up over conventional quantum computation. More promising than Bose\\ncondensates from the perspective of quantum computation and quantum communications,\\nis the use of cavity quantum electrodynamics to ‘dial up’ or synthesize arbitrary states\\nof the cavity field. Such a use of bosonic states is important for the field of quantum\\ncommunications, which requires the ability to create and manipulate entangled states of\\nthe electromagnetic field.\\n A third unconventional design for a quantum computer relies on ‘exotic’ statistics\\nthat are neither fermionic nor bosonic. Kitaev has recently proposed a quantum computer\\narchitecture based on ‘anyons,’ particles that when exchanged acquuire an arbitrary phase.\\nExamples of anyons include two-dimensional topological defects in lattice systems of spins\\nwith various symmetries. Kitaev noted that such anyons could perform quantum logic via\\nAharonov-Bohm type interactions [19]. Preskill et al. have shown explicitly how anyonic\\nsystems could compute in principle [20], and Lloyd et al. have proposed methods of\\nrealizing anyons using superconducting circuits (they could also in principle be constructed\\nusing NMR quantum computers to mock up the anyonic dynamics in an effectively two-\\ndimensional space of spins) [21]. The advantage of using anyons for quantum computation\\nis that their nonlocal topological nature can make them intrinsically error-correcting and\\nvirtually immune to the effects of noise and interference.\\n As the technologies of the microscale become better developed, more and more po-\\ntential designs for quantum computers, both conventional and unconventional, are likely\\nto arise. Additional technologies that could prove useful for the construction of quantum\\n\\n 4', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'de31f5a1-0290-45f3-b386-d02e3ed4c33c', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' logic devices include photonic crystals, optical hole-burning techniques, electron spin res-\\n onance, quantum dots, superconducting circuits in the quantum regime, etc. Since every\\n quantum degree of freedom can in principle participate in a computation one cannot a\\n priori rule out the possibility of using currently hard to control degrees of freedom such as\\nquark and gluon in complex nuclei to process information. Needless to say, most if not all\\nof the designs inspired by these technologies are likely to fail. There is room for optimism\\nthat some such quantum computer designs will prove practicable, however.\\n The preceding unconventional designs for quantum computers were based on existing,\\nexperimentally confirmed physical phenomena (except in the case of non-abelian anyons).\\nLet us now turn to designs based on speculative, hypothetical, and not yet verified phenom-\\nena. (One of the most interesting of these phenomena is large-scale quantum computation\\nitself: can we create and systematically transform entangled states involving hundreds or\\nthousands of quantum variables?) A particularly powerful hypothesis from the point of\\nview of quantum computation is that of nonlinear quantum mechanics.\\n The conventional picture of quantum mechanics is that it is linear in the sense that the\\nsuperposition principle is obeyed exactly. (Of course, quantum systems can still exhibit\\nnonlinear interactions between degrees of freedom while continuing to obey the superpo-\\nsition principle.) Experiment confirms that the superposition principle is indeed obeyed\\nto a high degree of accuracy. Nonetheless, a number of scientists including Weinberg have\\nproposed nonlinear versions of quantum mechanics in which the superposition principle\\nis violated. Many of these proposals exhibit pathologies such as violations of the second\\nlaw of thermodynamics or the capacity for superluminal communication. Despite such\\ntheoretical difficulties, it is still possible that quantum mechanics does indeed possess a\\nsmall nonlinearity, even if it currently seems unlikely. If a nonlinear operation such as\\nthat proposed by Weinberg can be incorporated in a quantum logic operation, then the\\nconsequences are striking: NP-complete problems can be solved easily in polynomial time\\n[17]. Indeed, NP-oracle problems and all problems in #P can be solved in polynomial time\\non such a nonlinear quantum computer.\\n A general proof of this result is given in [17], however, a simple argument for why\\nthis is so can be seen as follows. Suppose that it is possible to perform a non-unitary\\noperation on a single qubit that has a positive Lyapunov exponent over some region: i.e.,\\nsomewhere on the unit sphere there exists a line of finite extent along which application of\\nthe operation causes nearby points to move apart exponentially at a rate eλ∆θ proportional\\n 5', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '0777fa16-f0d4-4ad9-9877-a4abca0ba70f', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'to their original angular separation δθ. Now consider a function f (x) from N bits to one\\nbit. We wish to determine whether or not there exists an x such that f (x) = 1, and if\\nso, how many such x’s there are. Using the nonlinear operation with positive Lyapunov\\nexponent, it is straightforward to construct a mapping leaves a point on the exponentially\\nexpanding line (call this point |0〉) fixed if their are no solutions to the equation f (x) = 1,\\nand that maps the point to a nearby point cos(n/2N )|0〉 + sin(n/2N )|1〉 along the line\\nif there are exactly n solutions to the equation f (x) = 1. Repeated application of the\\nnonlinear map can be used to drive the points apart at an exponentional rate: eventually,\\nat a time determined by the number of qubits N , the number of solutions n, and the rate\\nof spreading λ, the two points will become macroscopically distinguishable, allowing one\\nto determine whether or not there is a solution and if there is, how many solutions there\\nare. The map f need only be applied once, and the amount of time it takes to reveal the\\nnumber of solutions is proportional to N .\\n The fact that nonlinear quantum mechanics allows the straightforward solution of\\nNP-complete and #P problems should probably be regarded as yet another strike against\\nnonlinear quantum mechanics. Whether or not quantum mechanics is linear is a question\\nto be resolved experimentally, however. In the unlikely event that quantum mechanics\\ndoes turn out to be nonlinear, all our problems may be solved.\\n Finally, let us turn our attention to hypothetical quantum Theories of Everything,\\nsuch as string theory. Such a theory must clearly support quantum computation since it\\nsupports cavity quantum electrodynamics and nuclear magnetic resonance. The obvious\\nquestion to ask is then, does a Theory of Everything need to support anything more than\\nquantum computation? So far as experimental evidence is concerned the answer to this\\nquestion is apparently No: we have no evident reason to doubt that the universe is at\\nbottom anything more than a giant, parallel, quantum information processing machine,\\nand that the phenomena that we observe and attempt to characterize are simply outputs\\nof this machine’s ongoing computation. Of course, just how the universe is carrying out\\nthis computation is likely to remain a question of great interest for some time.\\n To summarize: Computers are physical systems, and what they can do in practice and\\nin principle is circumscribed by the laws of physics. The laws of physics in turn permit a\\nwide variety of quantum computational devices including some based on nonconventional\\nstatistics and exotic effects. Modifications made to the laws of physics have the consequence\\nthat what can be computed in practice and in principle changes. A particularly intriguing\\n\\n 6', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'd315d076-2b6e-4ec4-ade1-e18fb6c09998', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'variation on conventional physics is nonlinear quantum mechanics which, if true, would\\nallow hard problems to be solved easily.\\n\\n 7', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '7cc3c174-6e77-4928-af58-522265cace2a', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'References\\n\\n1. P. Benioff, ‘Quantum Mechanical Models of Turing Machines that Dissipate No Energy,’\\nPhysical Review Letters, Vol. 48, No. 23, pp. 1581-1585 (1982)\\n2. D. Deutsch, ‘Quantum Theory, the Church-Turing Principle and the Universal Quantum\\nComputer,’ Proceedings of the Royal Society of London, A, Vol. 400, pp. 97-117 (1985).\\n3. R.P. Feynman, ‘Quantum Mechanical Computers,’ Optics News, Vol. 11, pp. 11-20\\n(1985); also in Foundations of Physics, Vol. 16, pp. 507-531 (1986).\\n4. S. Lloyd, ‘A Potentially Realizable Quantum Computer,’ Science, Vol. 261, pp. 1569-\\n1571 (1993).\\n5. J.I. Cirac and P. Zoller, ‘Quantum Computations with Cold Trapped Ions,’ Physical\\nReview Letters, Vol. 74, pp. 4091-4094 (1995).\\n6. Q.A. Turchette, C.J. Hood, W. Lange, H. Mabuchi, H.J. Kimble, ‘Measurement of\\nConditional Phase Shifts for Quantum Logic,’ Physical Review Letters, Vol. 75, pp. 4710-\\n4713 (1995).\\n7. C. Monroe, D.M. Meekhof, B.E. King, W.M. Itano, D.J. Wineland, ‘Demonstration of\\na Fundamental Quantum Logic Gate,’ Physical Review Letters, Vol. 75, pp. 4714-4717\\n(1995).\\n8. D.G. Cory, A.F. Fahmy, T.F. Havel, ‘Nuclear Magnetic Resonance Spectroscopy: an\\nexperimentally accessible paradigm for quantum computing,’ in PhysComp96, Proceedings\\nof the Fourth Workshop on Physics and Computation, T. Toffoli, M. Biafore, J. Le˜ao, eds.,\\nNew England Complex Systems Institute, 1996, pp. 87-91.\\n9. N.A. Gershenfeld and I.L. Chuang, ‘Bulk Spin-Resonance Quantum Computation,’\\nScience, Vol. 275, pp. 350-356 (1997).\\n10. P. Shor, ‘Algorithms for Quantum Computation: Discrete Log and Factoring,’ in\\nProceedings of the 35th Annual Symposium on Foundations of Computer Science, S. Gold-\\nwasser, Ed., IEEE Computer Society, Los Alamitos, CA, 1994, pp. 124-134.\\n11. R.P. Feynman, ‘Simulating Physics with Computers,’ International Journal of Theo-\\nretical Physics, Vol. 21, pp. 467-488 (1982).\\n12. S. Lloyd, ‘Universal Quantum Simulators,’ Science, Vol. 273, pp. 1073-1078 (1996).\\n13. L.K. Grover, ‘Quantum Mechanics Helps in Searching for a Needle in a Haystack,’\\nPhysical Review Letters, Vol. 79, pp. 325-328 (1997).\\n14. D. Deutsch, A. Barenco, A. Ekert, ‘Universality in Quantum Computation,’ Proceed-\\nings of the Royal Society of London A, Vol. 449, pp. 669-677 (1995).\\n\\n 8', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'a9610b7b-a167-4f21-a917-3626fbddde3b', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Unconventional Quantum Computing Devices'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' 15. S. Lloyd, ‘Almost Any Quantum Logic Gate is Universal,’ Physical Review Letters,\\n Vol. 75, pp. 346-349 (1995).\\n 16. S. Lloyd, ‘Fermionic Quantum Computers,’ talk delivered at the Santa Barbara work-\\n shop on Physics of Information, November 1996.\\n 17. D. Abrams and S. Lloyd, to be published.\\n 18. J. Preskill et al., to be published.\\n19. Yu. Kitaev, to be published.\\n20. J. Preskill et al., to be published.\\n21. S. Lloyd et al. to be published.\\n\\n 9', 'text_template': '{metadata_str}\\n\\n{content}'}]}\n", + "--------------------------------------------------\n", + "Transition number: 5\n", + "Transition type: finish_branch\n", + "Transition output: {'documents': [{'class_name': 'Document', 'doc_id': 'be0e8bc0-0012-40bf-90b8-0e50f67a0433', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'arXiv:quant-ph/0407030v1 5 Jul 2004\\n\\n On quantum state of entangled photon pairs\\n Ruo Peng WANG\\n Department of Physics, Peking University, Beijing 100871, P.R.China\\n Email: rpwang@cis.pku.edu.cn\\n June 18, 2018\\n\\n Abstract\\n I show that the photon pairs used in experimental tests of quantum\\n non-locality based on Bell’s theorem are not in the entangled quantum\\n state. The correct quantum state of the “entangled” photon pairs is sug-\\n gested. Two experiments for testing this quantum state are proposed.\\n PACS: 03.65.Ud, 42.50.Dv\\n Keywords: entangled photon pair, entangled quantum states, quantum\\n non-locality\\n\\n 1 introduction\\n Quantum non-locality is a controversial topic of quantum theory, and entangled\\n photon pairs played a very important role in experimental tests of quantum non-\\n locality. So far, a number of experimental tests of quantum non-locality based\\n on Bell’s theorem [1] have been carried out [2, 3, 4, 5, 6]. In these experiments\\n entangled photon pairs are produced, and the polarization correlation between\\n entangled photon pairs is measured. Experimental results obtained in these tests\\n are in favor for quantum non-locality. Although it has been pointed out that\\n this polarization correlation is compatible with local realism [7], these results\\n are generally considered as direct evidences for the existence of quantum non-\\n locality, and more experiments [8, 9, 10] showing different kind of quantum\\n non-local correlation are carried out. The entangled photon pairs are used as\\n light source in these experiments, and the interpretation of the experimental\\n results as proofs for quantum non-locality is closely related to the assumption\\n that these photon pairs are in entangled quantum state.\\n In this letter, I will show that the same polarization correlation also exists\\n for certain un-entangled photon pairs. And on other hand, based on the consid-\\n eration of momentum conservation, I will also show that the entangled quantum\\n state is not a correct description for photon pairs used in these experimental\\n tests of quantum non-locality. A correct quantum state for these photon pairs is\\n suggested, and two experiments for testing this quantum state and the quantum\\n non-locality are proposed.\\n\\n 1', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '0e51993f-ad65-4849-aaa5-3718b2edf1c8', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '2 polarization correlation between un-entangled\\n photons\\nLet’s consider a beam of un-entangled photon pairs with each photon pair con-\\ntaining a left circle polarized photon and a right circle polarized photon. Such\\na beam of photon pairs can be produced by adjusting polarization state of\\nthe photon pairs obtained from the parametric down conversion with a type-II\\ncollinear phase matching [11]. The quantum state of this un-entangled photon\\npairs can be expressed as\\n |ψ〉 = 2 1(bv †+ ibh†)(bv †− ibh†)|0〉 = 2 1(bv †bv †+ bh†bh†)|0〉, (1)\\nwhere bv† and bh are the creation operator for vertically polarized photon and,†\\nrespectively, for horizontally polarized photon with the wave vector~2. By using k\\nthe Coulomb gauge, we may describe the optical field of these photon pairs by\\nthe vector potential A~. The positive frequencies part of the vector potential is\\nrelated to the photon annihilation operators in the following way\\n A+ = B(bv~v + bh~h)ei~2 ·~\\n ~ e e k r (2)\\nwhere the constant B is given by [12]\\n B =√ μ0ℏc2 (3)\\n 2V ω\\nwith μ0 the magnetic permeability of the vacuum, ω the angular frequency of\\nthe photons.\\n We divide the beam of un-entangled photon pairs into two channels by using\\na half reflecting mirror (BS). The wave vectors of photons in these two channels\\nare~1 and k k ~2. A half wave plate (W P1) is placed into the channel 1 to introduce\\na phase difference of π between the vertical and horizontal components of the\\noptical field, and another half wave plate (W P2) is placed into the channel 2\\nto swap the vertical and horizontal components of the optical field, as shown\\nin FIG. 1. As no incident photons with the wave vector~1 being present, after k\\npassing the beam splitter and the wave plates, the optical field becomes [13]\\n A~ + = √2 (bv~v − bh~h)ei~1 ·~ + √2 (bh~v + bv~h)ei~2 ·~r .Be e k r B e e k (4)\\nThe photons in the channel 1 and 2 are polarized by Glan-Thompsom linear\\npolarization analyzers P1 and P2, respectively, before being detected by single\\nphoton detectors D1 and D2.\\n To analyze the polarization correlation between photons detected by detec-\\ntors D1 and D2, we need expressions of optical field at these detectors. Up to\\na phase factor, we have\\n A+~θ1 = √2 (bv cos θ1 − bh sin θ1)~θ1B e (5)\\n 2', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '99eaa171-c4c6-43bb-8b9c-6deeffc5c6fc', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' 1 D1\\n 2\\n 1(b bh\\n 2 h † †+b bvv † †) 0 1 2 WP1 θ1\\n 1 P1\\n 1 θ2\\n 2 BS 2\\n 1 WP2\\n 2 2 P2\\n 1 D2\\n\\nFigure 1: A schematic illustration of the experimental setup for testing the\\npolarization correlation between un-entangled photon pairs. The polarization\\nstates of optical beams are indicated by arrows.\\n\\nand B\\n A+~θ2 = √2 (bh cos θ2 + bv sin θ2)~θ2 .e (6)\\nIn the above expressions, ~θ1 and ~θ2 are vectors of unit of the transmissione e\\naxes of the analyzer P1, and respectively, P2. The relations (5) and (6) can be\\nrewritten in the following form\\n A+~θ1 = √2 bθ1 ~θ1 , AθB e ~+2 = √2 bθ2~θ2B e (7)\\nwhere\\n bθ1 = bv cos θ1 − bh sin θ1, bθ2 = bh cos θ2 + bv sin θ2 (8)\\nare the annihilation operators for the photon polarized in the direction ~θ1 withe\\nthe wave vector~1 and, respectively, for the photon polarized in the direction k\\n~θ2 with the wave vector~2.\\ne k\\n The coincidence counting rate C(θ1, θ2) is proportional to the probability\\nof annihilating simultaneously one photon polarized in the direction ~θ1 at thee\\ndetector D1 and one photon polarized in the direction ~θ2 at the detector D2.e\\nThis probability is, in its turn, proportional the matrix element\\n 〈ψ|bθ2 b†1 bθ1bθ2|ψ〉 = 〈ψ|(bv † †cos θ1 − bh sin θ1)(bh cos θ2 + bvsin θ2)† † †\\n θ\\n × (bv cos θ1 − bh sin θ1)(bh cos θ2 + bv sin θ2)|ψ〉 (9)\\n = 4 1sin2(θ1 − θ2).\\nTherefore we have C(θ1, θ2) = CM sin2(θ1 − θ2), (10)\\n\\n 3', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': 'f98c627e-47a9-42c3-83bb-6111f9b06525', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'where CM this the maxim counting rate that occurs at θ1 − θ2 = ±π/2. This\\ncorrelation is just the same as in the case of entangled photon pairs [6, 11], and\\nthe Bell’s inequality is apparently violated also in the above discussed experi-\\nment. The violation of the Bell’s inequality in this case is only apparent, because\\na condition for obtaining the Bell’s inequality is not fulfilled here. Namely, in\\nthe experiment described in this paper, both photons of a photon pair could\\nbe detected in one single channel, with the wave vector k~1 or k~2. Thus, even\\nthough the same polarization correlation as in the case of entangled photon\\npairs is realized in this experiment, no quantum non-locality is involved here.\\nIndeed, after annihilating of a linear polarized photon at the detector D1, the\\nquantum state becomes\\n |ψ′〉 = bθ1 |ψ〉 = 1√2 (cos θ1b† − sin θ1b†)|0〉.v h (11)\\nThe photon in the quantum state |ψ′〉 is not spatially separated from the detector\\nD1 where another photon was annihilated. The probability as the photon in\\nthe quantum state |ψ′〉 being detected at D1 is\\n 〈ψ′|bθ1 bθ1 |ψ′〉 = 2 † 1. (12)\\n3 quantum state of entangled photon pairs\\nNow we arrive at a very important point: the observation of a photon polar-\\nization correlation as expressed in the relation (10) does not form a sufficient\\nevidence for quantum non-locality. Besides this correlation, one must exclude\\nthe possibility of detecting both of the photons in one single channel to ensure\\nthat the phenomena of quantum non-locality are really observed.\\n An apparently non-local correlation as expressed in the relation (10) are ob-\\nserved experimentally for the entangled photon pairs generated in the paramet-\\nric down conversion nonlinear optical processes with a type-II phase matching.\\nWe have to see if the possibility of detecting both of the photons in one single\\nchannel can be ruled out in the experiment so that one can really interpret the\\nexperimental results as an experimental evidence for quantum non-locality. So\\nfar the following quantum state is used to describe this photon pair[11]:\\n |ψe〉 = 1√2 (bv†1b†2 − bvh †2b†1)|0〉,h (13)\\nwhere bh1, b†1, b†2 and bv2 are creation operators for horizontally polarized pho-† †\\n v h\\ntons with the wave vector k~1, for vertically polarized photons with the wave\\nvector~1, for horizontally polarized photons with the wave vector k k ~2, and for\\nvertically polarized photons with the wave vector~2, respectively. By using the k\\ncorresponding photon annihilation operators, we may write the optical field as\\n A+ = B(bv1~v + bh1~h)ei~1 ·~ + B(bv2~v + bh2~h)ei~2 ·~.\\n ~e e e k r e e k r (14)\\n\\n 4', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '288c7d8f-ef47-4768-b601-8119e00d4d55', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': 'It is easy to verify that there exists a correlation as described by the relation\\n(10) between the polarization of the photon pairs in the quantum state |ψe〉.\\n The expression (13) for the quantum state of the entangled photon pairs was\\nderived by taking into consideration of the energy and the momentum conserva-\\ntion in the parametric down conversion process. The energy and the momentum\\nof a photon pair in the state |ψe〉 is equal to the energy and the momentum of\\nthe incident photon from which the photon pair is generated. But there is\\na problem in this argument, namely the parametric down conversion process\\ntakes place in nonlinear optical crystals, so one must take the photons and the\\ncrystal as one single system when the condition of the energy and the momen-\\ntum conservation is applied. The macroscopic properties of nonlinear optical\\ncrystals do not change in the parametric down conversion process. Thus the\\nexpectation values of the energy and the momentum of crystals are the same\\nbefore and after the photon pairs’ generation. Therefore the expectation val-\\nues of energy and momentum of a photon pair must equal to the expectation\\nvalues of energy and momentum of incident photon. This condition is satisfied\\nby the quantum state |ψe〉. On other hand, photons are coupled with optical\\nphonons with same momentum when passing through crystals, and there exist\\nexchanges of energy and momentum between photons and crystals through the\\nphoton-phonon interaction during photons’ propagation in crystals. One may\\nneglect the energy exchange between photons and crystals because the ener-\\ngies of phonons are much smaller than that of photons with same momentum,\\nbut the momentum exchange between photons and crystals must be taken into\\nconsideration. This exchange of momentum causes fluctuations in momenta of\\ncrystals and generated photon pairs. But the state |ψe〉 is an eigen-state of the\\nphoton pairs’ momentum, that means there is not momentum fluctuations for\\nphoton pairs in the state |ψe〉. Therefore the generated photon pair can not be\\nin this quantum state.\\n According to the calculation made before, the correlation between the po-\\nlarization of the photon pairs as described by the relation (10) does exist also\\nfor photon pairs in the following quantum state\\n |ψu〉 = 2 1(b1 †+ ib2†)(b1 †− ib2†)|0〉 = 2 1(b1†b1 †+ b2†b2†)|0〉, (15)\\nwith the optical field given by\\n A~u += √2 (b1~v − b2~h)ei~1 ·~ + √2 (b2~v + b1~h)ei~2 ·~r .B e e k r B e e k (16)\\nThe quantum state |ψu〉 is an un-entangled state. In fact, we can write |ψu〉 as\\n |ψu〉 = |ψa〉 ⊗ |ψb〉, (17)\\nwith †|0〉, |ψb〉 = bb†|0〉,\\n |ψa〉 = ba (18)\\nwhere †= 1 †= 1\\n ba √2 (b1 †+ ib2†), bb √2 (b1 †− ib2†), [ba†, bb] = 0. (19)\\n 5', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '43b30d97-ad73-4167-b142-fec2d80f5ce9', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' The quantum state |ψu〉 is not an eigen-state of momentum, but photon\\npairs in the quantum states |ψe〉 and |ψu〉 have the same expectation values of\\nthe energy and the momentum. So the quantum state |ψu〉 satisfies the energy\\nand momentum conservation requirement. In conclusion, the quantum state\\n|ψu〉 could be a correct description for photon pairs generated in the paramet-\\nric down conversion nonlinear optical processes with a type-II phase matching.\\nOne may observe that for photon pairs in the quantum state |ψu〉, both photons\\ncould be detected in one single channel. Therefore, by applying the conditions\\nof the energy and momentum conservation, we can not rule out the possibility\\nof detecting both photons in one single channel. But, instead, we find that the\\nphoton pairs can not be in the entangled state. The apparently non-local cor-\\nrelation between the polarization of photon pairs produced in the parametric\\ndown conversion nonlinear optical processes, that were believed being in entan-\\ngled states, is not a proof for the existence of the quantum non-locality, but just\\na necessary evidence for the fact that photon pairs are in the quantum state\\n|ψu〉.\\n The same conclusion holds also for photon pairs emitted in a radiative atomic\\ncascade of calcium [2, 3]. In that process, electrons which emit two photons in a\\nradiative cascade are well confined within the ions of calcium. The uncertainty\\nin the momentum of electrons implies that the photon pairs can not be in the\\nentangled state which has a well defined momentum. The quantum state for\\nphoton pairs generated in this process could be expressed as\\n |ψu′〉 = 1√2 (bωhb†2h+ bω† 1 ω †1v b†2v )|0〉,ω (20)\\nwhere bω1h, b†1v are creation operators for horizontally, and respectively, ver-†ω\\ntically polarized photons of circle frequency ω1, and bω2h, b†2v are the same† ω\\noperators for photons of circle frequency ω2.\\n The optical field in the channel 1 is given by\\n A+= g11(bω1 v~v + bωh~h)eicω1~1 ·~ + g12(bω2v~v + bω2h~h)eicω2~n1·~r\\n ~1 e 1 e n r e e (21)\\nand\\n A+= g21(bω1 v~v + bωh~h)eicω1~1 ·~ + g22(bω2v~v + bω2h~h)eicω2~n·~r\\n ~2 e e n r e e 1 (22)\\n 1\\nin the channel 2, where ~1, ~2 are vectors of unity that indicate the propaga-n n\\ntion directions of the channels 1 and 2, and g11, g12, g21, g22 are coefficients that\\ndepend on the geometry of the experiment setup. We can now calculate the\\nprobability of annihilating simultaneously a photon of circle frequency ω1 po-\\nlarized in the direction ~θ1 in the channel 1 and a photon of circle frequency ω2e\\npolarized in the direction ~θ2 in the channel 2. By using the expressions (20),e\\n(21) and (22), we find\\n C12(θ1, θ2) = C′M cos2(θ1 − θ2). (23)\\nThis is exactly the correlation observed in experimental tests carried out by\\nAspect et al. [2, 3]. Again, no quantum non-locality is evolved, and both of the\\nphotons can be detected in one single channel also in these tests.\\n\\n 6', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '120d303e-23a4-49c2-8a47-0a738f91d646', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' D1\\n θ1\\n 1 P1\\n “entangled”\\n photon pair 2 BS θ4\\n 4\\n P3 3 P4\\n θ3 D4\\n D3\\n\\nFigure 2: A schematic illustration of the experimental setup for testing the\\npossibility of detecting both photons from one “entangled” photon pair in one\\nsingle channel. The polarization states of optical beams are indicated by arrows.\\n\\n4 proposal for experimental tests\\nDirect experimental tests on the possibility of detecting both photons in one\\nsingle channel and on the coherence of photons in different channels could make\\nour conclusions on the quantum state of “entangled” photons pairs and quan-\\ntum non-locality more convincing. An experimental test on the possibility of\\ndetecting both of the photons in one single channel can be done by using an\\nexperimental setup shown in FIG. 2. This setup is quite similar to that used\\nfor tests of quantum non-locality based on Bell’s theorem. But a half reflecting\\nmirror is inserted now into the channel 2 to split it into the channels 3 and 4,\\nand the coincidence counting rate between the channels 3 and 4 is measured.\\nThis coincidence counting rate is proportional to the polarization correlation\\nbetween photons in the channel 3 and in the channel 4. An anti-coincidence\\ncondition with the signal from the channel 1 can also be applied to ensure that\\nthis coincidence counting rate is not from other sources. By using the expres-\\nsions (15), (16), (13) and (14), one can easily verify that the following relation\\nholds for this coincidence counting rate\\n C(θ3, θ4) = CMcos2(θ3 − θ4),′′ (24)\\nif the photon pairs are in the quantum state |ψu〉, and\\n\\n C(θ3, θ4) ≡ 0, (25)\\nif the photon pairs are in the quantum state |ψe〉.\\n The coherence of photons in different channels can also be used for testing\\nthe quantum state of “entangled” photon pairs. An experimental setup for such\\na test is schematically illustrated in FIG. 3. The linear polarizers P1 and P2 are\\n\\n 7', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '7a329a04-32f6-4bd5-911c-1f787517abfb', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': ' M1\\n P1\\n 1 WP D\\n “entangled”\\n photon pair 2\\n (x,y)\\n P2\\n M2\\n\\nFigure 3: A schematic illustration of the experimental setup for testing the\\ncoherence between photons from one “entangled” photon pair in different chan-\\nnels. The polarization states of optical beams are indicated by arrows.\\n\\ninserted into the beams of “entangled” photon pairs, generated in the parametric\\ndown conversion nonlinear optical processes with a type-II phase matching, in\\nsuch a way, so that the photon in the channel 1 becomes horizontally polarized,\\nwhile the photon in the channel 2 is polarized vertically. The polarization of\\nthe beam 1 is changed to vertical later by the half wave plate W P . Both beams\\nare reflected by the mirrors M1 and M2 to overlap each other. The single\\nphoton counting rate I(x, y) in the (x, y) plan as a function of the coordinates\\n(x, y) is measured by using the single photon detector D. Let f1(x, y)~v bee\\nthe distribution of the vector potential of the beam 1 in the plan (x, y), and\\nf2(x, y)~v the distribution of the vector potential of the beam 2 . If the photone\\npair were in the quantum state |ψe〉, we have\\n A+(x, y) = bh1f1(x, y)~v + bv2f2(x, y)~v .\\n ~ e e (26)\\n The single photon counting rate I(x, y) is proportional to the matrix element\\n I(x, y) ∝ 〈ψe|~+†(x, y) · A+(x, y)|ψe〉. A~ (27)\\nBy using the relation (26) and the expression (13) for |ψe〉, we find, for photon\\npairs in the quantum state |ψe〉,\\n I(x, y) ∝ |f1(x, y)|2 + |f2(x, y)|2, (28)\\nso no interference occurs. But if the photon pairs were in the quantum state\\n|ψu〉, then according to Eq.(16), we have\\n A+(x, y) = b2f1(x, y)~v + b2f2(x, y)~v .\\n ~ e e (29)\\nAnd in this case the counting rate becomes\\n I(x, y) ∝ |f1(x, y) + f2(x, y)|2, (30)\\nthat means the beam 1 and beam 2 prepared in the way described above are\\ncoherent.\\n\\n 8', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '8e86a476-b4c4-4c21-b244-d82f87eca2a4', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '5 discussion\\nI have shown that the correlations of photons’ polarization observed in “en-\\ntangled” photon pairs generated in the parametric down conversion nonlinear\\noptical processes with a type-II phase matching and in a radiative atomic cas-\\ncade of calcium are not proofs for quantum non-locality. Instead, they are\\nnecessary evidences for the fact that “entangled” photon pairs are in the un-\\nentangled states |ψu〉 or |ψu′〉. According to the expression (16) for the vector\\npotential, in the case of “entangled” photon pairs generated in the parametric\\ndown conversion nonlinear optical processes with a type-II phase matching, we† and b2 †in terms of operators bv1, bv† †2, bh1 and bh2:† †\\nmay express the operators b1\\n b1 = 1† √2 (bv1 + bh2), b2 † † †= 1√2 (bv2 − bh1).† † (31)\\nWe have then\\n |ψu〉 = 1√2 |ψe〉 + 4 1(bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉.† v † v † h † h (32)\\nAs the component (bv1b†1 + bv2b†2 + bh1b†1 + bh2b†2)|0〉 in |ψu〉 has no contri-† † † †\\n v v h h\\nbution to the coincidence counting rate between the signals from the channel 1\\nand the channel 2, all apparently non-local correlations that were believed as\\nspecificity of photon pairs in the entangled state |ψe〉, occur also in the case of\\nun-entangled photon pairs in the state |ψu〉. The same conclusion holds also\\nfor “entangled” photon pairs generated in a radiative atomic cascade of cal-\\ncium Therefore no physical phenomena that necessitate introducing quantum\\nnon-locality for their explanation are really observed.\\n Einstein, Podolsky, Rosen (EPR) and Bohm had put the completeness of\\nquantum mechanics in contradiction to the relativistic causality by supposing\\nthe existence of particle pairs in entangled quantum states [14, 15]. But till\\nnow, such a contradiction did not occur, because no particle pairs in entangled\\nquantum states had been produced. Can particle pairs in entangled quantum\\nstates be generated ever? It is most likely not. Due to the interaction with\\nthe source of particle pairs, it should be impossible for the produced particle\\npair with different momenta to be in a quantum state with well defined mo-\\nmentum, such as the entangled quantum state. This observation is consistent\\nwith Santos’s suggestion [7] that only quantum states which do not contradict\\nwith locality requirement are physical states. From this point of view, the EPR\\nparadox is just a spectacular illustration of the restriction on quantum states\\nimposed by locality requirement.\\n\\nReferences\\n[1] J. S. Bell, Physics 1, 195 (1964)\\n[2] A. Aspect, P. Grangier, and G. Roger, Phys. Rev. Lett. 49, 91 (1981)\\n\\n 9', 'text_template': '{metadata_str}\\n\\n{content}'}, {'class_name': 'Document', 'doc_id': '170e2b68-f50d-4a3b-8155-79c7b12a3da1', 'embedding': None, 'end_char_idx': None, 'excluded_embed_metadata_keys': [], 'excluded_llm_metadata_keys': [], 'extra_info': {'file_name': 'Quantum State of Entangled Photon Pairs'}, 'metadata_seperator': '\\n', 'metadata_template': '{key}: {value}', 'mimetype': 'text/plain', 'relationships': {}, 'start_char_idx': None, 'text': '[3] A. Aspect, J. Dalibard, and G. Roger, Phys. Rev. Lett. 49, 1804 (1981)\\n[4] Z. Y. Ou, and L. Mandel, Phys. Rev. Lett. 61, 50 (1988)\\n[5] Y. H. Shih, and C. O. Alley, Phys. Rev. Lett. 61, 2921 (1988)\\n[6] Y. H. Shih, A. V. Sergienko, Morton H. Rubin, T. E. Kiess and C. O. Alley,\\n Phys. Rev. A 50, 23 (1994)\\n[7] E. Santos, Phys. Rev. A 46, 3646 (1992)\\n[8] D. V. Strekalov, A. V. Sergienko, D. N. Klyshko and Y. H. Shih, Phys. Rev.\\n Lett. 74, 3600 (1995)\\n[9] E. J. S. Fonseca, P. H. Souto Ribeiro, S. P´adua and C. H. Monken, Phys.\\n Rev. A 60, 1530 (1999)\\n[10] Z. Zhao, et al., Phys. Rev. Lett. 91, 180401 (2003)\\n[11] P. G. Kwiat et al., Phys. Rev. Lett. 75, 4337 (1995)\\n[12] F. Mandl and G. Shaw, Quantum field theory (John Wiley and Sons, Chich-\\n ester, 1984), Chap. 1\\n[13] L. Mandel and E. Wolf, Optical Coherence and Quantum Optics (Cam-\\n bridge University Press, 1995), p. 640\\n[14] A. Einstein, B. Pdolsky, and N. Rosen, Phys. Rev. 47, 777 (1935)\\n[15] D. Bohm, Quantum Theory (Prentice Hall, Englewood Cliffs, NJ, 1951)\\n\\n 10', 'text_template': '{metadata_str}\\n\\n{content}'}]}\n", + "--------------------------------------------------\n", + "Transition number: 6\n", + "Transition type: finish_branch\n", + "Transition output: {'documents': []}\n", + "--------------------------------------------------\n", + "Transition number: 7\n", + "Transition type: finish_branch\n", + "Transition output: {'documents': []}\n", + "--------------------------------------------------\n", + "Transition number: 8\n", + "Transition type: init_branch\n", + "Transition output: ['Unconventional Quantum Computing Devices', 'https://arxiv.org/pdf/quant-ph/0003151v1']\n", + "--------------------------------------------------\n", + "Transition number: 9\n", + "Transition type: init_branch\n", + "Transition output: ['Quantum State of Entangled Photon Pairs', 'https://arxiv.org/pdf/quant-ph/0407030v1']\n", + "--------------------------------------------------\n", + "Transition number: 10\n", + "Transition type: init_branch\n", + "Transition output: ['Quantum-dot based photonic quantum networks', 'https://arxiv.org/pdf/1707.02094v1']\n", + "--------------------------------------------------\n", + "Transition number: 11\n", + "Transition type: init_branch\n", + "Transition output: ['The Rise of Quantum Internet Computing', 'https://arxiv.org/pdf/2208.00733v1']\n", + "--------------------------------------------------\n", + "Transition number: 12\n", + "Transition type: init_branch\n", + "Transition output: ['Three-electron spin qubits', 'https://arxiv.org/pdf/1611.09106v1']\n", + "--------------------------------------------------\n", + "Transition number: 13\n", + "Transition type: step\n", + "Transition output: {'paper_titles_and_urls': [['The Rise of Quantum Internet Computing', 'https://arxiv.org/pdf/2208.00733v1'], ['Unconventional Quantum Computing Devices', 'https://arxiv.org/pdf/quant-ph/0003151v1'], ['Quantum-dot based photonic quantum networks', 'https://arxiv.org/pdf/1707.02094v1'], ['Quantum State of Entangled Photon Pairs', 'https://arxiv.org/pdf/quant-ph/0407030v1'], ['Three-electron spin qubits', 'https://arxiv.org/pdf/1611.09106v1']]}\n", + "--------------------------------------------------\n", + "Transition number: 14\n", + "Transition type: step\n", + "Transition output: The Rise of Quantum Internet Computing,http://arxiv.org/pdf/2208.00733v1,,Unconventional Quantum Computing Devices,http://arxiv.org/pdf/quant-ph/0003151v1,,Quantum-dot based photonic quantum networks,http://arxiv.org/pdf/1707.02094v1,,Quantum State of Entangled Photon Pairs,http://arxiv.org/pdf/quant-ph/0407030v1,,Three-electron spin qubits,http://arxiv.org/pdf/1611.09106v1\n", + "--------------------------------------------------\n", + "Transition number: 15\n", + "Transition type: step\n", + "Transition output: {'persona': 'This is a test google scholar persona. The user is a 25 year old living in San Francisco. He is a fan of Google Scholar and is a student at Stanford University. He uses Google Scholar to stay updated with the latest research in quantum computing and artificial intelligence. Over the last 3 months, he has read 100+ research papers on quantum computing and artificial intelligence. He is particularly interested in the latest research in quantum machine learning and quantum error correction. He is also interested in how LLMs are being used to solve problems in quantum mechanics.'}\n", + "--------------------------------------------------\n", + "Transition number: 16\n", + "Transition type: step\n", + "Transition output: [{'content': ['This is a test google scholar persona. The user is a 25 year old living in San Francisco. He is a fan of Google Scholar and is a student at Stanford University. He uses Google Scholar to stay updated with the latest research in quantum computing and artificial intelligence. Over the last 3 months, he has read 100+ research papers on quantum computing and artificial intelligence. He is particularly interested in the latest research in quantum machine learning and quantum error correction. He is also interested in how LLMs are being used to solve problems in quantum mechanics.'], 'created_at': '2024-11-28T20:14:58.425970Z', 'embeddings': [[-0.015671737492084503, -0.016573749482631683, 0.00606738543137908, 0.02482711337506771, -0.02882998250424862, 0.027073346078395844, 0.0021525435149669647, -0.004192618653178215, -0.020146284252405167, -0.009691162966191769, -0.027398928999900814, -0.0018318545771762729, -0.06293833255767822, 0.010626989416778088, -0.010664321482181547, -0.029900139197707176, 0.020602896809577945, 0.02405634708702564, 0.05098491162061691, 0.01597627066075802, 0.0018167400266975164, 0.018538368865847588, 0.024278147146105766, -0.029432903975248337, 0.027721546590328217, 0.008752194233238697, -0.049808528274297714, -0.05644961819052696, 0.016817443072795868, 0.014144135639071465, -0.03128163889050484, 0.02094954438507557, -0.0010352752869948745, 0.00638605747371912, 0.0025517295580357313, 0.028167083859443665, 0.011125176213681698, -0.06921841949224472, -0.01873999275267124, 0.0031037251465022564, -0.03698429465293884, -0.025046246126294136, 0.02133142203092575, 0.04462399706244469, -0.016854416579008102, -0.0430510975420475, 0.007649009115993977, -0.005883590318262577, -0.004722981248050928, -0.003350751241669059, 0.001046227524057031, 0.005026552826166153, -0.018029887229204174, 0.053446244448423386, -0.04077441990375519, 0.02201766148209572, -0.04088416695594787, 0.02015075832605362, -0.010267499834299088, -0.03215021640062332, -0.02580904401838779, 0.002094632713124156, 0.015541240572929382, -0.01390778087079525, -0.011648902669548988, -0.03873421996831894, 0.04925611615180969, -0.002199657494202256, 0.0007072139997035265, -0.009419391863048077, -0.004543200135231018, 0.016517797484993935, -0.0038691842928528786, -0.0003323692944832146, 0.0013189563760533929, -0.04027409106492996, -0.026864318177103996, -0.0419197753071785, -0.04853703454136848, -0.04465369880199432, 0.10594317317008972, 0.046854328364133835, -0.016298886388540268, 0.0028036185540258884, -0.02735450491309166, 0.00727339182049036, -0.02162793092429638, -0.04689304158091545, 0.03303921967744827, -0.04478146880865097, -0.050480831414461136, -0.0022575948387384415, 0.06019856780767441, -0.007690425962209701, -0.007917585782706738, -0.0030607846565544605, 0.008670471608638763, -0.005760575644671917, 0.04050808399915695, -0.01663600094616413, -0.08409805595874786, -0.0545378103852272, 0.028721405193209648, -0.0274882297962904, -0.025321273133158684, -0.03201734647154808, 0.08263128250837326, 0.027298621833324432, 0.022652707993984222, -0.008065377362072468, -0.04547583311796189, 0.00024730083532631397, 0.006774285342544317, -0.005536885466426611, 0.01815061829984188, -0.011350437067449093, -0.013348687440156937, -0.03891073539853096, -0.008982078172266483, -0.0110185444355011, 0.006448717787861824, -0.003866349812597037, -0.01446465030312538, -0.017594661563634872, -0.021710380911827087, 0.04407104104757309, 0.015940461307764053, 0.000976060691755265, -0.00647510914131999, 0.04934041574597359, -0.007258467376232147, 0.004550923127681017, -0.007780217099934816, 0.004065909422934055, 0.09547679126262663, 0.04624375328421593, 0.017912067472934723, 0.023161662742495537, 0.04640576615929603, -0.040533095598220825, -0.03486146405339241, 0.03730496019124985, 0.014086959883570673, -0.03051741048693657, -0.022120190784335136, 0.017715398222208023, 0.023322880268096924, -0.01310406904667616, -0.0023814246524125338, -0.018863970413804058, 0.006086794193834066, -0.00048064111615531147, -0.01596466265618801, 0.017894890159368515, 0.05033557862043381, -0.01838282123208046, -0.020436692982912064, -0.00957989227026701, -0.01178071554750204, -0.005518375895917416, 0.01110754068940878, 0.05040781944990158, 0.05383875221014023, -0.0071881492622196674, -0.006425702478736639, -0.031881414353847504, -0.00022720034758094695, 0.006209813989698887, -0.023442106321454048, -0.016704266890883446, 0.04915720224380493, 0.027567364275455475, 0.0012057595886290071, 0.017430562525987625, -0.05634449794888496, 0.04888565465807915, 0.012403023429214954, 0.017480600625276566, -0.023227309808135033, 0.027199234813451767, -0.01250576600432396, -0.02900809980928898, -0.014700164087116718, -0.02383880503475666, -0.00781917478889227, -0.036619704216718674, 0.018626833334565163, 0.014100673608481884, -0.005629208404570818, -0.022381072863936424, 0.05632047355175018, -0.019722633063793182, 0.0177643783390522, 0.01912875100970268, -0.013510119169950483, 0.0017246071947738528, -0.0025881659239530563, -0.04659343883395195, -0.03099522367119789, -0.04312900826334953, 0.02855907753109932, -0.004508837126195431, -0.020491929724812508, -0.0044746133498847485, -0.014226329512894154, 0.05342676490545273, -0.0689413920044899, -0.009655713103711603, 0.02880263514816761, -0.04532184451818466, 0.02293764427304268, 0.020767733454704285, 0.0023432194720953703, 0.000435711961472407, -0.007493944838643074, -0.01006926316767931, 0.04915594682097435, -0.03558000549674034, -0.010591919533908367, -0.014236132614314556, 0.0019505976233631372, -0.04549582302570343, 0.03677915409207344, -0.017518380656838417, -0.001874879002571106, 0.03888753801584244, -0.0400361567735672, 0.03189687803387642, -0.015013301745057106, 0.042322590947151184, -0.0348396822810173, 0.007443180773407221, -0.018349964171648026, 0.012257285416126251, 0.0405883714556694, 0.032246921211481094, -0.005144959781318903, -0.00998249650001526, -0.00861453264951706, -0.0015368403401225803, -0.02094422467052937, -0.02368479035794735, 0.044471438974142075, 0.012644251808524132, 0.016110071912407875, -0.021831434220075607, 0.021487608551979065, 0.02189694717526436, -0.007880999706685543, 0.009051802568137646, -0.020886454731225967, -0.04215766489505768, 0.02852765843272209, 0.010385671630501749, 0.006151126231998205, 0.013935141265392303, 0.0446663461625576, -0.05859478563070297, -0.039108756929636, 0.07105202227830887, -0.02975363656878471, -0.03637517988681793, 0.032182589173316956, -0.03530033305287361, 0.01657995767891407, -0.002660004887729883, 0.03197391703724861, -0.013362894766032696, -0.01032151561230421, -0.05690392851829529, -0.03784545511007309, 0.05079466104507446, 0.018589600920677185, 0.0455319918692112, 0.021151592954993248, 0.007274063769727945, 0.026385176926851273, -0.022956445813179016, 0.04503209888935089, -0.01282365433871746, -0.03962119668722153, -0.05242256447672844, -0.02195855602622032, -0.02395829930901527, -0.018755154684185985, 0.016288159415125847, -0.009570167399942877, 0.02655772492289543, -0.01149686798453331, -0.01733289659023285, -0.01132907159626484, 0.010648725554347038, -0.027164166793227196, 0.08130868524312973, 0.10823941975831984, 0.02648075297474861, 0.03431148827075958, 0.008908306248486042, -0.014196693897247314, -0.035078711807727814, -0.06461808830499649, 0.026893718168139458, 0.011442850343883038, 0.03250885382294655, 0.017624935135245323, 0.011857464909553528, 0.002460518153384328, 0.03818918392062187, -0.0063593690283596516, -0.021517546847462658, 0.0010228778701275587, 0.008773728273808956, -0.01058958936482668, 0.016834473237395287, 0.00774932699277997, -0.013765799812972546, -0.020262477919459343, 0.049936212599277496, -0.05347384884953499, -0.02195303700864315, 0.055521052330732346, -0.04548721760511398, 0.021305428817868233, 0.011855010874569416, 0.02747522108256817, 0.03386770933866501, 0.03813450038433075, -0.012636431492865086, 0.009854207746684551, -0.026002028957009315, -0.016509123146533966, 0.017511950805783272, 0.0101975966244936, 0.009558593854308128, -0.024949507787823677, 0.042774662375450134, -0.010284076444804668, -0.021528080105781555, 0.06304275989532471, -0.005416685715317726, 0.04142051190137863, -0.061019305139780045, 0.0021855514496564865, 0.012258350849151611, -0.03472227230668068, -0.03312791511416435, -0.000548225361853838, -0.03286008909344673, 0.014545919373631476, -0.04137556254863739, 0.01812741346657276, -0.031323935836553574, -0.0013330738293007016, -0.0038443892262876034, 0.02350905165076256, 0.02289889939129353, 0.04909924417734146, 0.0023193976376205683, 0.022132596001029015, -0.0007981927483342588, -0.0130745405331254, -0.011024853214621544, -0.01922866329550743, 0.02994532696902752, -0.02510892227292061, -0.0017747505335137248, -0.013762572780251505, -0.012294976972043514, 0.0029200813733041286, -0.04524457827210426, 0.024902673438191417, -0.010699890553951263, 0.014746712520718576, 0.011174866929650308, -0.003198194084689021, 0.0115395225584507, -0.05107438936829567, -0.03408464789390564, 0.0248948335647583, 0.08031124621629715, -0.007413153536617756, 1.1204278052900918e-05, 0.032477203756570816, 0.02424856834113598, -0.04604659602046013, -0.008982615545392036, 0.02713506855070591, -0.00014915135398041457, -0.04647098481655121, 0.05307076498866081, -0.04728394001722336, 0.008045613765716553, -0.012928247451782228, -0.0283577349036932, 0.04300623387098312, 0.006467579398304224, -0.010231422260403631, 0.033360861241817474, 0.06968259066343307, -0.015288825146853924, -0.03302546218037605, -0.003182978369295597, -0.018235858529806137, -0.037918929010629654, 0.031039569526910785, -0.06398001313209534, -0.03387216851115227, -0.008716827258467674, 0.02346736006438732, -0.03328898549079895, -0.01363233383744955, 0.007253660354763269, -0.05993199720978737, 0.002409775508567691, 0.02767910249531269, -0.009428243152797222, 0.004192461725324392, 0.01593441143631935, 0.010803882032632828, -0.03209521621465683, 0.056819044053554535, -0.025737211108207703, 0.026851141825318336, 0.03652720898389816, 0.07137501984834671, 0.02381859160959721, -0.02629965543746948, 0.01646084524691105, 0.0402849018573761, 0.06080068275332451, -0.010235550813376904, 0.045287467539310455, 0.023535894230008125, 0.019999904558062553, 0.06872355192899704, 0.002749955514445901, 0.04185028746724129, -0.014117076992988586, 0.004449980799108744, 0.022451188415288925, -0.08766558766365051, -0.01968243159353733, -0.014860468916594982, 0.011703227646648884, -0.03528899699449539, 0.04094488546252251, -0.0182349756360054, -0.010647347196936607, -0.0068405563943088055, -0.03713414445519447, 0.01734321378171444, -0.014805512502789496, 0.0039975522086024284, -0.031015098094940186, -0.040302954614162445, -0.004620896652340889, 0.033191826194524765, 0.016461864113807678, -0.0099693201482296, -0.03457574173808098, -0.0032087538857012987, 0.05708996579051018, 0.01788393408060074, 0.007312337402254343, 0.00527529651299119, 0.023918433114886284, 0.04229012131690979, -0.03525855764746666, 0.0444633774459362, 0.015833482146263123, 0.02585923857986927, 0.005054720677435398, -0.018144117668271065, 0.03258787468075752, 0.09733619540929794, -0.0035987691953778267, -0.0030079844873398542, -0.004347142297774553, 0.006088628433644772, 0.008579614572227001, 0.02301499992609024, 0.031012961640954018, -0.028554832562804226, -0.04063648357987404, -0.004221928771585226, 0.013079751282930374, -0.008980480954051018, 0.05178505554795265, -0.009553750976920128, -0.020926352590322495, 0.06763507425785065, 0.01575387641787529, 0.006876692641526461, 0.012805862352252008, 0.026019291952252388, -0.053698718547821045, -0.030225062742829323, 0.01862126402556896, 0.018915286287665367, 0.004963831510394812, 0.001707061426714063, -0.028278857469558716, 0.011481338180601597, 0.10189607739448547, -0.011315464042127132, -0.030216041952371597, -0.009410486556589603, -0.04523077607154846, -0.03437221795320511, -0.032854508608579636, -0.006904524751007557, -0.01274321973323822, 0.04230444133281708, 0.005612626206129789, -0.004090940114110708, 0.00287990621291101, -0.015784408897161484, 0.036410365253686905, 0.03683935105800629, 0.026808004826307297, 0.02198147028684616, 0.008378231897950172, 0.027753980830311775, -0.032743874937295914, 0.016407456248998642, 0.01622433215379715, -0.001081481808796525, -0.04915321245789528, 0.027194306254386905, -0.02132691815495491, 0.022703934460878372, 0.001475967583246529, 0.06814076751470566, -0.006336662452667952, 0.024703048169612885, -0.037753745913505554, -0.00787679199129343, 0.005892901215702295, 0.021733757108449936, -0.036804307252168655, -0.04469569772481918, 0.0015878170961514115, 0.012951831333339214, 0.05260292440652847, 0.034867044538259506, 0.015943162143230438, 0.00021534621191676703, -0.02573549933731556, -0.022897690534591675, 0.0033477486576884985, -0.031493254005908966, -0.01227332279086113, 0.02139957807958126, -0.023974869400262833, 0.026824789121747017, -0.03917257860302925, -0.0008560992428101599, 0.027356544509530067, 0.05558615177869797, 0.01166608463972807, -0.05335056036710739, 0.07350723445415497, 0.04363447800278664, 0.0067238216288387775, 0.03043380007147789, 0.00388544169254601, -0.0426035039126873, -0.010510151274502276, -0.003422884736210108, 0.03230303153395653, -0.017875663936138153, -0.014806934632360935, -0.05547628179192543, -0.017292648553848267, 0.024563901126384735, 0.04695596545934677, -0.023022349923849102, 0.009652194567024708, -0.04497253894805908, 0.04430066794157028, -0.029072456061840057, -0.034856781363487244, -0.002577992854639888, 0.018187934532761577, 0.01606651023030281, 0.06916831433773041, -0.0611276738345623, 0.02651439607143402, -0.007505751214921475, 0.013727414421737194, -0.022345757111907005, -0.0241008959710598, 0.04046372324228287, 0.04941947013139725, -0.0743357241153717, -0.0036788349971175194, 0.024328691884875298, -0.02075951546430588, 0.053776901215314865, 0.026318199932575222, -0.02641713246703148, 0.005374014377593994, 0.005815513432025909, -0.02104218304157257, -0.000478421279694885, -0.004864947870373726, -0.01693611592054367, 0.006082981824874878, 0.02185416780412197, -0.004894257988780737, 0.044014349579811096, 0.007745668757706881, -0.05674390867352486, -0.05132412165403366, -0.003959771245718002, 0.04750882834196091, -0.08302006870508194, 0.022155405953526497, 0.004621697589755058, -0.01077416818588972, 0.01907477155327797, -0.028501665219664577, -0.031839556992053986, -0.03987284377217293, 0.04463058337569237, 0.02525818534195423, 0.04268491640686989, 0.05404660478234291, 0.026495562866330147, -0.05725787207484245, 0.06708839535713196, -0.040616001933813095, 0.007117047440260649, 0.010178884491324425, -0.0398554801940918, 0.005870613269507885, -0.01661405712366104, -0.029561053961515427, -0.02001872844994068, 0.02455179952085018, -0.010217715986073015, -0.048882585018873215, 0.019563745707273483, 0.03339054435491562, 0.012429753318428991, -0.018386399373412132, 0.008257194422185421, -0.09024550020694733, -0.007574629504233599, -0.017805302515625954, 0.021555552259087563, 0.03561445698142052, 0.025563770905137066, -0.05967013165354729, 0.03261229395866394, 0.020395640283823013, 0.0010519248899072409, -0.007264405488967896, -0.02988644689321518, -0.0006834609666839242, 0.009559893049299715, -0.06259236484766006, -0.04287950322031975, 0.02451629936695099, 0.005187939386814833, -0.020626986399292942, 0.006773501168936491, -0.0008871967438608408, -0.06348317116498947, -0.059235990047454834, 0.026502689346671104, 0.007882636040449142, -0.024022018536925316, 0.048618096858263016, 0.0005697319284081459, -0.007996258325874805, -0.006460745818912983, 0.007004959508776665, -0.019269634038209915, -0.013327757827937605, -0.010302916169166563, -0.04577208682894707, 0.07003707438707352, -0.017430966719985008, -0.04365025460720062, 0.01702691800892353, 0.029939400032162663, -0.017181357368826866, -0.05866405367851257, -0.004381679464131594, 0.01181634608656168, 0.056739985942840576, -0.0055499328300356865, 0.006000246852636337, 0.009496069513261318, -0.008567911572754383, -0.01812063530087471, -0.016472872346639633, 0.01089754980057478, -0.001624477794393897, -0.022375302389264107, 0.020380694419145584, 0.01826213300228119, 0.05061953887343407, 0.010463407263159752, 0.01178878266364336, -0.054011791944503784, 0.03485691919922829, -0.0038813326973468065, -0.016046656295657158, 0.04263216629624367, 0.019344231113791462, 0.01007376704365015, 0.009052429348230362, -0.004880830179899931, -0.005771951284259558, 0.009619758464396, -0.008930336683988571, -0.04444078356027603, -0.02321331389248371, -0.07236126065254211, 0.02756300568580627, -0.0627826377749443, -0.02186889201402664, -0.012462758459150791, 0.012121695093810558, -0.05031459033489227, -0.005573994014412165, -0.047674473375082016, -0.039457496255636215, 0.008656895719468594, -0.07709009200334549, 0.02961328811943531, 0.030352480709552765, 0.009597495198249815, 0.04613536223769188, -0.06183690205216408, 0.005994188133627176, -0.013949818909168243, -0.00688588572666049, 0.003567464416846633, -0.02375665493309498, 0.017575882375240326, -0.009050877764821053, 0.02715885080397129, -0.0275584626942873, -0.00565616600215435, 0.059069324284791946, 0.01723213493824005, 0.01944643631577492, -0.029830878600478172, 0.02430517226457596, 0.008780461736023426, -0.02558065578341484, 0.015310601331293585, 0.008585300296545029, -0.037155088037252426, 0.030778761953115463, -0.03061114065349102, 0.0007349445950239897, -0.003309677354991436, -0.03539082035422325, 0.024017760530114177, -0.02482817694544792, 0.0065100667998194695, -0.0123089412227273, 0.011266719549894331, -0.026769552379846573, 0.05635958164930344, -0.029568519443273544, 0.023208744823932648, -0.009875490330159664, 0.005224302876740694, 0.02364432066679001, -0.047307029366493225, 0.029456112533807755, 0.000765095348469913, 0.0005293125286698341, 0.0027997419238090515, -0.008478994481265545, -0.004502691328525543, -0.018787603825330738, 0.025373129174113274, -0.03243747353553772, -0.016425473615527153, -0.03484295308589935, 0.04218599200248718, 0.007117601577192545, 0.04108959436416626, -0.037058353424072266, -0.001522526959888637, -0.014897141605615616, 0.011060012504458427, 0.08031164109706879, 0.009770511649549007, -0.029838593676686287, 0.006582826841622591, -0.006129378918558359, -0.01629176177084446, 0.04512185975909233, 0.032675664871931076, 0.031846772879362106, 0.06536265462636948, 0.007312471978366375, 0.01310885976999998, -0.009925377555191515, -0.028877153992652893, -0.03552689775824547, 0.022310003638267517, -0.017354223877191544, 0.013240499421954157, -0.04407158121466637, -0.020975349470973015, 0.0022642838302999735, -0.00013966132246423513, 0.007005855441093445, -0.026748422533273697, 0.00787591002881527, 0.0008165257750079036, 0.009265893138945104, -0.04001495987176895, 0.05231707543134689, -0.011505059897899628, 0.009689347818493845, 0.009744562208652496, 0.0010478353360667825, -0.007743769325315952, -0.0332467257976532, -0.014588418416678904, -0.005152959376573563, 0.015856411308050156, 0.018252084031701088, 0.07453695684671402, 0.00655342685058713, 0.08186939358711243, 0.00596515741199255, -0.02105704694986343, 0.013323825784027576, 0.042618297040462494, 0.029993180185556412, -0.014195147901773453, 0.03501703217625618, -0.05326278135180473, 0.001281377277337015, 0.026922041550278664, -0.05839579924941063, -0.002660108497366309, -0.0024291155859827995, -0.0021973242983222008, -0.04514843598008156, -0.02748742327094078, -0.03665287047624588, 0.06646118313074112, 0.06781721115112305, 0.0331895649433136, -0.02882961556315422, 0.011725064367055891, -0.01110695768147707, 0.054598987102508545, -0.014716672711074352, 0.020555704832077023, 0.04091455414891243, 0.07123471796512604, -0.003848938504233956, 0.013824107125401495, -0.0239957757294178, -0.06283079087734222, 0.04493340849876404, 0.016829051077365875, 0.034249015152454376, -0.01664159633219242, 0.02629677578806877, -0.06096959859132767, -0.021724019199609756, -0.0009781391127035022, -0.01892434060573578, 0.027962829917669296, 0.04807991161942482, 0.011118643917143343, -0.05037091299891472, 0.019078891724348068, 0.025122957304120064, -0.012683769688010216, 0.029236873611807823, -0.05762153863906861, 0.05900304764509201, 0.002422163262963295, -0.024205945432186127, 0.011141522787511349, 0.003424206515774131, 0.01915670558810234, 0.0032873237505555153, 0.008867895230650902, 0.0009473440586589276, 0.020629115402698517, -0.03235945850610733, -0.042592603713274, 0.009217322804033756, 0.04201772436499595, 0.05268913507461548, -0.046489328145980835, 0.02806694060564041, -0.02565360628068447, -0.0497392863035202, -0.016092391684651375, 0.0006345878937281668, -0.04294111579656601, -0.011590949259698393, 0.030646147206425667, -0.02217712067067623, -0.039698999375104904, 0.05597437918186188, 0.011473299004137516, 0.023486362770199776, 0.016497135162353516, 0.005261394195258617, 0.007188127376139164, -0.0716647207736969, 0.054382696747779846, 0.007359266746789217, 0.00479888217523694, 0.05038231983780861, -0.023093674331903458, 0.0208931565284729, 0.008188041858375072, 0.0360475592315197, 0.054475609213113785, -0.011876086704432964, 0.029267925769090652, 0.004166736733168364, 0.02817683108150959, -0.039146341383457184, -0.018799390643835068, 0.05607893690466881, -0.009280548430979252, 0.02381298877298832, -0.003631530562415719, -0.02611156925559044, 0.00863554049283266, 0.016642767935991287, 0.015563145279884338, -0.027823247015476227, 0.010765188373625278, -0.07269980758428574, 0.022374359890818596, 0.0070311324670910835, 0.010940865613520144, 0.02748931385576725, -0.07200745493173599, -0.003260524244979024, 0.0006049862131476402, 0.006680012680590153, 0.021251065656542775, 0.014500902034342287, -0.04386712610721588, 0.0682847872376442, 0.0022957678884267807, -0.016259783878922462, 0.0575750432908535, 0.0014137334655970335, 0.05061626434326172, -0.006252502556890249, 0.0140424482524395, -0.00560104101896286, -0.03719421476125717, -0.051093701273202896, -0.0056787761859595776, -0.028819678351283073, 0.0024047743063420057, -0.014415109530091286, -0.02083515003323555, -0.005331628490239382, 0.0005036098882555962, -0.01817723736166954, 0.014249456115067003, 0.02692011557519436, 0.04013052210211754, -0.013481054455041884, -0.001688574207946658, -0.0145127447322011, 0.06283353269100189, -0.04194600135087967, -0.03789134696125984, -0.03999098390340805, -0.06151798367500305, -0.03374876827001572, 0.05555419251322746, 0.001248985645361245, 0.0032316388096660376, 0.01928131841123104, -0.003295362927019596, -0.011491600424051285, 0.0027902687434107065, 0.03794625774025917, -0.0057970150373876095, -0.003453561570495367, 0.020120754837989807, 0.010037431493401527, -0.02036227658390999, 0.008080268278717995, -0.012845122255384922, 0.020809661597013474, 0.006917924620211124, 0.0133863789960742, -0.028565330430865288, 0.0048051257617771626, 0.020031211897730827, -0.04113258793950081, -0.0028436570428311825, -0.0017412119777873158, 0.06099452078342438, 0.0324663482606411, 0.0514092855155468, -0.008131146430969238, 0.02120067924261093, -0.032824523746967316, 0.004918454214930534, 0.01862029917538166, 0.046941712498664856, 0.09854447841644288, 0.052336569875478745, -0.001825400977395475, -0.037958212196826935, 0.03428357094526291]], 'id': 'ec3af6b0-b2a6-4d84-8987-ddffafc0516e', 'metadata': {}, 'title': 'Google Scholar Persona'}]\n", + "--------------------------------------------------\n", + "Transition number: 17\n", + "Transition type: step\n", + "Transition output: {'user_info': {'about': 'This is a test user', 'created_at': '2024-11-28T20:14:56.748977Z', 'id': '70ba3a2c-b5ab-4074-8f01-fe9f81771c17', 'metadata': {'age': 25, 'city': 'San Francisco', 'favorite_scientists': \"['Richard Feynman', 'Alan Turing', 'Geoffrey Hinton']\", 'latest_research_read': 'Quantum Machine Learning', 'ppid': '70ba3a2c-b5ab-4074-8f01-fe9f81771c17', 'research_interests': \"['Quantum Computing', 'Artificial Intelligence', 'Machine Learning']\", 'state': 'California', 'top_journals': \"['Nature', 'Science', 'IEEE Transactions on Quantum Engineering']\", 'top_research_area': 'Quantum Computing', 'top_scientist': 'Richard Feynman'}, 'name': 'user_new_1', 'updated_at': '2024-11-28T20:14:56.748167Z'}}\n", + "--------------------------------------------------\n", + "Transition number: 18\n", + "Transition type: step\n", + "Transition output: [{'about': 'This is a test user', 'created_at': '2024-11-28T20:14:56.748977Z', 'id': '70ba3a2c-b5ab-4074-8f01-fe9f81771c17', 'metadata': {'age': 25, 'city': 'San Francisco', 'favorite_scientists': \"['Richard Feynman', 'Alan Turing', 'Geoffrey Hinton']\", 'latest_research_read': 'Quantum Machine Learning', 'ppid': '70ba3a2c-b5ab-4074-8f01-fe9f81771c17', 'research_interests': \"['Quantum Computing', 'Artificial Intelligence', 'Machine Learning']\", 'state': 'California', 'top_journals': \"['Nature', 'Science', 'IEEE Transactions on Quantum Engineering']\", 'top_research_area': 'Quantum Computing', 'top_scientist': 'Richard Feynman'}, 'name': 'user_new_1', 'updated_at': '2024-11-28T20:14:56.748167Z'}]\n", + "--------------------------------------------------\n", + "Transition number: 19\n", + "Transition type: step\n", + "Transition output: {'arxiv_results': [{'result': [{'authors': ['Seng W. Loke'], 'categories': ['cs.ET', 'cs.DC'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/2208.00733v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2208.00733v1', 'http://arxiv.org/pdf/2208.00733v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2208.00733v1', 'primary_category': 'cs.ET', 'published': '2022-08-01 10:36:13', 'summary': 'This article highlights quantum Internet computing as referring todistributed quantum computing over the quantum Internet, analogous to(classical) Internet computing involving (classical) distributed computing overthe (classical) Internet. Relevant to quantum Internet computing would be areasof study such as quantum protocols for distributed nodes using quantuminformation for computations, quantum cloud computing, delegated verifiableblind or private computing, non-local gates, and distributed quantumapplications, over Internet-scale distances.', 'title': 'The Rise of Quantum Internet Computing', 'updated': '2022-08-01 10:36:13'}, {'authors': ['Seth Lloyd'], 'categories': ['quant-ph'], 'comment': '9 pages, plain TeX, article from 1998 conference proceedings\\n proposing fermionic quantum computers', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0003151v1', 'journal_ref': \"in `Unconventional Models of Computation,' C.S. Calude, J. Casti,\\n M.J. Dinneen, eds., Springer, Singapore, 1998\", 'links': ['http://arxiv.org/abs/quant-ph/0003151v1', 'http://arxiv.org/pdf/quant-ph/0003151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0003151v1', 'primary_category': 'quant-ph', 'published': '2000-03-31 22:07:23', 'summary': \"This paper investigates a variety of unconventional quantum computationdevices, including fermionic quantum computers and computers that exploitnonlinear quantum mechanics. It is shown that unconventional quantum computingdevices can in principle compute some quantities more rapidly than`conventional' quantum computers.\", 'title': 'Unconventional Quantum Computing Devices', 'updated': '2000-03-31 22:07:23'}]}, {'result': [{'authors': ['Shiqi Gong', 'Shuaiqiang Liu', 'Danny D. Sun'], 'categories': ['q-fin.PM'], 'comment': '35 pages, 7 figures, 6 tables', 'doi': None, 'entry_id': 'http://arxiv.org/abs/2306.02764v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2306.02764v1', 'http://arxiv.org/pdf/2306.02764v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2306.02764v1', 'primary_category': 'q-fin.PM', 'published': '2023-06-05 10:40:53', 'summary': \"Market making plays a crucial role in providing liquidity and maintainingstability in financial markets, making it an essential component ofwell-functioning capital markets. Despite its importance, there is limitedresearch on market making in the Chinese stock market, which is one of thelargest and most rapidly growing markets globally. To address this gap, weemploy an optimal market making framework with an exponential CARA-type(Constant Absolute Risk Aversion) utility function that accounts for variousmarket conditions, such as price drift, volatility, and stamp duty, and iscapable of describing 3 major risks (i.e., inventory, execution and adverseselection risks) in market making practice, and provide an in-depthquantitative and scenario analysis of market making in the Chinese stockmarket. Our numerical experiments explore the impact of volatility on themarket maker's inventory. Furthermore, we find that the stamp duty rate is acritical factor in market making, with a negative impact on both the profit ofthe market maker and the liquidity of the market. Additionally, our analysisemphasizes the significance of accurately estimating stock drift for managinginventory and adverse selection risks effectively and enhancing profit for themarket maker. These findings offer valuable insights for both market makers andpolicymakers in the Chinese stock market and provide directions for furtherresearch in designing effective market making strategies and policies.\", 'title': 'Optimal Market Making in the Chinese Stock Market: A Stochastic Control and Scenario Analysis', 'updated': '2023-06-05 10:40:53'}, {'authors': ['Zhongyang Zhao', 'Caisheng Wang', 'Huaiwei Liao', 'Carol J. Miller'], 'categories': ['eess.SY', 'cs.SY'], 'comment': '51st North American Power Symposium, October 2019', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1910.14515v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1910.14515v1', 'http://arxiv.org/pdf/1910.14515v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1910.14515v1', 'primary_category': 'eess.SY', 'published': '2019-10-31 14:55:25', 'summary': \"A competitive wholesale electricity market consists of thousands ofinteracting market participants. Driven by the variations of fuel costs, systemloads and weathers, these market participants compete actively and behavevariously in the power market. Although electricity markets tend to become moretransparent, a large amount of market information is still not publiclyavailable to market participants. Hence, data-driven analysis based on publicdata is crucial for market participants to better understand and modellarge-scale power markets, and ultimately to perform better in power trading.While most of the previous researches related to the large-scale power marketsare based on the synthetic networks, a data-driven approach utilizing the realpower market data is proposed in this paper. First, the power plants' monthlynet generation and capacity data are obtained from U.S. Energy InformationAdministration (EIA) and aggregated to figure out the monthly regional capacityfactors which are used to characterize the market's regional behaviors formarket participants. Then, the regional capacity factors are analyzed againstthe metered system loads and natural gas prices to study the generationbehaviors in the power market. The analysis reveals the impacts of regionalnatural gas prices on capacity factors and the responses of generatingbehaviors to the system loads. The analysis results present the solid evidenceand rational references for market participants to model and validate thelarge-scale power market in the future.\", 'title': 'Data-driven Analysis of Regional Capacity Factors in a Large-Scale Power Market: A Perspective from Market Participants', 'updated': '2019-10-31 14:55:25'}]}, {'result': [{'authors': ['Peter Lodahl'], 'categories': ['quant-ph'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1707.02094v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1707.02094v1', 'http://arxiv.org/pdf/1707.02094v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1707.02094v1', 'primary_category': 'quant-ph', 'published': '2017-07-07 09:19:15', 'summary': 'Quantum dots embedded in photonic nanostructures have in recent years provento be a very powerful solid-state platform for quantum optics experiments. Thecombination of near-unity radiative coupling of a single quantum dot to aphotonic mode and the ability to eliminate decoherence processes imply that anunprecedented light-matter interface can be obtained. As a result,high-cooperativity photon-emitter quantum interfaces can be constructed openinga path-way to deterministic photonic quantum gates for quantum-informationprocessing applications. In the present manuscript, I review currentstate-of-the-art on quantum dot devices and their applications for quantumtechnology. The overarching long-term goal of the research field is toconstruct photonic quantum networks where remote entanglement can bedistributed over long distances by photons.', 'title': 'Quantum-dot based photonic quantum networks', 'updated': '2017-07-07 09:19:15'}, {'authors': ['Ruo Peng Wang'], 'categories': ['quant-ph'], 'comment': '3 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0407030v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/quant-ph/0407030v1', 'http://arxiv.org/pdf/quant-ph/0407030v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0407030v1', 'primary_category': 'quant-ph', 'published': '2004-07-05 08:43:07', 'summary': \"I show that the photon pairs used in experimental tests of quantumnon-locality based on Bell's theorem are not in the entangled quantum state.The correct quantum state of the ``entangled'' photon pairs is suggested. Twoexperiments for testing this quantum state are proposed.\", 'title': 'Quantum State of Entangled Photon Pairs', 'updated': '2004-07-05 08:43:07'}]}, {'result': [{'authors': ['Pierre Paleo', 'Alessandro Mirone'], 'categories': ['physics.comp-ph', 'cs.CV'], 'comment': 'IUCR template, preprint mode, 35 figures', 'doi': '10.1107/S1600577515010176', 'entry_id': 'http://arxiv.org/abs/1502.01480v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1107/S1600577515010176', 'http://arxiv.org/abs/1502.01480v1', 'http://arxiv.org/pdf/1502.01480v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1502.01480v1', 'primary_category': 'physics.comp-ph', 'published': '2015-02-05 09:51:59', 'summary': 'We present a novel approach to handle ring artifacts correction in compressedsensing tomographic reconstruction. The correction is part of thereconstruction process, which differs from classical sinogram pre-processingand image post-processing techniques. The principle of compressed sensingtomographic reconstruction is presented. Then, we show that the ring artifactscorrection can be integrated in the reconstruction problem formalism. Weprovide numerical results for both simulated and real data. This technique isincluded in the PyHST2 code which is used at the European Synchrotron RadiationFacility for tomographic reconstruction.', 'title': 'Ring artifacts correction in compressed sensing tomographic reconstruction', 'updated': '2015-02-05 09:51:59'}, {'authors': ['Rasmus Dalgas Kongskov', 'Yiqiu Dong'], 'categories': ['cs.CE'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1708.06912v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1708.06912v1', 'http://arxiv.org/pdf/1708.06912v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1708.06912v1', 'primary_category': 'cs.CE', 'published': '2017-08-23 08:08:47', 'summary': 'Decomposition of tomographic reconstructions has many different practicalapplication. We propose two new reconstruction methods that combines the taskof tomographic reconstruction with object decomposition. We demonstrate thesereconstruction methods in the context of decomposing directional objects intovarious directional components. Furthermore we propose a method for estimatingthe main direction in a directional object, directly from the measured computedtomography data. We demonstrate all the proposed methods on simulated and realsamples to show their practical applicability. The numerical tests show thatdecomposition and reconstruction can combined to achieve a highly usefulfibre-crack decomposition.', 'title': 'Tomographic Reconstruction Methods for Decomposing Directional Components', 'updated': '2017-08-23 08:08:47'}]}, {'result': [{'authors': ['Liang Liu', 'Hongwei Qin', 'Jifan Hu'], 'categories': ['cond-mat.mes-hall'], 'comment': '11 pages, 8 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1904.04991v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1904.04991v1', 'http://arxiv.org/pdf/1904.04991v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1904.04991v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2019-04-10 03:37:14', 'summary': 'By the means of screened exchange density functional theory, we find that thephosphorene nanoribbons with bare zigzag edges that undergo Peierls distortionis a antiferromagnetic semiconductor in which the polarized states are mainlylocalized at the edges. Under application of external electric fields, thephosphorene nanoribbons present exotic electronic structure transitionsincluding semiconducting, half-metallic, and metallic states.', 'title': 'Exotic Electronic Property of Phosphorene Nanoribbons Driven by External Electric Field', 'updated': '2019-04-10 03:37:14'}, {'authors': ['Young Jin Kim', 'Ping-Han Chu', 'Igor Savukov', 'Shaun Newman'], 'categories': ['hep-ex', 'physics.atom-ph'], 'comment': '17 pages, 5 figures', 'doi': '10.1038/s41467-019-10169-1', 'entry_id': 'http://arxiv.org/abs/1902.00128v2', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1038/s41467-019-10169-1', 'http://arxiv.org/abs/1902.00128v2', 'http://arxiv.org/pdf/1902.00128v2'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1902.00128v2', 'primary_category': 'hep-ex', 'published': '2019-01-31 23:35:06', 'summary': 'Exotic spin-dependent interactions between fermions have recently attractedattention in relation to theories beyond the Standard Model. The exoticinteractions can be mediated by hypothetical fundamental bosons which mayexplain several unsolved mysteries in physics. Here we expand this area ofresearch by probing an exotic parity-odd spin- and velocity-dependentinteraction between the axial-vector electron coupling and the vector nucleoncoupling for polarized electrons. This experiment utilizes a high-sensitivityatomic magnetometer, based on an optically polarized vapor that is a source ofpolarized electrons, and a solid-state mass containing unpolarized nucleons.The atomic magnetometer can detect an effective magnetic field induced by theexotic interaction between unpolarized nucleons and polarized electrons. We setan experimental limit on the electron-nucleon coupling $g_\\\\text{A}^\\\\text{e}g_\\\\text{V}^\\\\text{N}<$ $10^{-30}$ at the mediator boson mass below $10^{-4}$ eV,significantly improving the current limit by up to 17 orders of magnitude.', 'title': 'Experimental limit on an exotic parity-odd spin- and velocity-dependent interaction using an optically polarized vapor', 'updated': '2019-04-24 21:40:25'}]}, {'result': [{'authors': ['R. Jackiw'], 'categories': ['hep-th'], 'comment': '13 pages, LaTeX with 2 figures (available from author), MIT-CTP #2371', 'doi': None, 'entry_id': 'http://arxiv.org/abs/hep-th/9410151v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/hep-th/9410151v1', 'http://arxiv.org/pdf/hep-th/9410151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9410151v1', 'primary_category': 'hep-th', 'published': '1994-10-20 20:21:54', 'summary': \"25th anniversary and new building dedication Centre de RecherchesMath\\\\'{e}matiques Montr\\\\'{e}al, Canada, October 1994\", 'title': 'My Encounters --- as a Physicist --- with Mathematics', 'updated': '1994-10-20 20:21:54'}, {'authors': ['R. Jackiw'], 'categories': ['hep-th', 'hep-ph', 'physics.hist-ph', 'quant-ph'], 'comment': 'Email correspondence to jackiw@mitlns.mit.edu ; 4 pages, LaTeX', 'doi': '10.1073/pnas.95.22.12776', 'entry_id': 'http://arxiv.org/abs/hep-th/9709212v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1073/pnas.95.22.12776', 'http://arxiv.org/abs/hep-th/9709212v1', 'http://arxiv.org/pdf/hep-th/9709212v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9709212v1', 'primary_category': 'hep-th', 'published': '1997-09-29 20:50:54', 'summary': 'The present-day crisis in quantum field theory is described.', 'title': 'What is quantum field theory and why have some physicists abandoned it?', 'updated': '1997-09-29 20:50:54'}]}, {'result': [{'authors': ['Maximilian Russ', 'Guido Burkard'], 'categories': ['cond-mat.mes-hall'], 'comment': '42 pages, 13 figures', 'doi': '10.1088/1361-648X/aa761f', 'entry_id': 'http://arxiv.org/abs/1611.09106v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1088/1361-648X/aa761f', 'http://arxiv.org/abs/1611.09106v1', 'http://arxiv.org/pdf/1611.09106v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.09106v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2016-11-28 13:21:43', 'summary': 'The goal of this article is to review the progress of three-electron spinqubits from their inception to the state of the art. We direct the main focustowards the resonant exchange (RX) qubit and the exchange-only qubit, but wealso discuss other qubit implementations using three electron spins. For eachthree-spin qubit we describe the qubit model, the physical realization, theimplementations of single-qubit operations, as well as the read-out andinitialization schemes. Two-qubit gates and decoherence properties arediscussed for the RX qubit and the exchange-only qubit, thereby, completing thelist of requirements for a viable candidate qubit implementation for quantumcomputation. We start with describing the full system of three electrons in atriple quantum dot, then discuss the charge-stability diagram and restrictourselves to the relevant subsystem, introduce the qubit states, and discussimportant transitions to other charge states. Introducing the various qubitimplementations, we begin with the exchange-only qubit, followed by thespin-charge qubit, the hybrid qubit, and the RX qubit, discussing for each thesingle-qubit operations, read-out, and initialization methods, whereas the mainfocus will be on the RX qubit, whose single-qubit operations are realized bydriving the qubit at its resonant frequency in the microwave range similar toelectron spin resonance. Two different types of two-qubit operations arepresented for the exchange-only and the RX qubit which can be divided intoshort-ranged and long-ranged interactions. Both of these interaction types canbe expected to be necessary in a large-scale quantum computer. We also takeinto account the decoherence of the qubit through the influence of magneticnoise as well as dephasing due to charge noise.', 'title': 'Three-electron spin qubits', 'updated': '2016-11-28 13:21:43'}, {'authors': ['Sam Young Cho', 'Mun Dae Kim'], 'categories': ['cond-mat.supr-con', 'cond-mat.mes-hall'], 'comment': '5 pages, 1 figure', 'doi': '10.1103/PhysRevB.77.212506', 'entry_id': 'http://arxiv.org/abs/cond-mat/0703505v1', 'journal_ref': 'Phys. Rev. B 77, 212506 (2008)', 'links': ['http://dx.doi.org/10.1103/PhysRevB.77.212506', 'http://arxiv.org/abs/cond-mat/0703505v1', 'http://arxiv.org/pdf/cond-mat/0703505v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/cond-mat/0703505v1', 'primary_category': 'cond-mat.supr-con', 'published': '2007-03-20 02:20:35', 'summary': 'Superconducting flux qubits are considered to investigate macroscopicmany-qubit interactions. Many-qubit states based on current states can be manipulated through thecurrent-phase relation in each superconducting loop. For flux qubit systems comprised of $N$ qubit loops, a general expression oflow energy Hamiltonian is presented in terms of low energy levels of qubits andmacroscopic quantum tunnelings between the many-qubit states. Many-qubit interactions classified by {\\\\em Ising type- or tunnel-}exchangeinteractions can be observable experimentally. Flux qubit systems can provide various artificial-spin systems to studymany-body systems that cannot be found naturally.', 'title': 'Macroscopic Many-Qubit Interactions in Superconducting Flux Qubits', 'updated': '2007-03-20 02:20:35'}]}, {'result': [{'authors': ['Henry Cohn'], 'categories': ['math.MG', 'math.NT'], 'comment': '24 pages, 9 figures', 'doi': '10.1090/noti1474', 'entry_id': 'http://arxiv.org/abs/1611.01685v1', 'journal_ref': 'Notices Amer. Math. Soc. 64 (2017), no. 2, 102-115', 'links': ['http://dx.doi.org/10.1090/noti1474', 'http://arxiv.org/abs/1611.01685v1', 'http://arxiv.org/pdf/1611.01685v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.01685v1', 'primary_category': 'math.MG', 'published': '2016-11-05 18:16:28', 'summary': \"This expository paper describes Viazovska's breakthrough solution of thesphere packing problem in eight dimensions, as well as its extension totwenty-four dimensions by Cohn, Kumar, Miller, Radchenko, and Viazovska.\", 'title': 'A conceptual breakthrough in sphere packing', 'updated': '2016-11-05 18:16:28'}, {'authors': ['Jesper W. Schneider', 'Rodrigo Costas'], 'categories': ['cs.DL'], 'comment': 'Accepted for publication in Journal of the Association for\\n Information Science and Technology', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1512.01388v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1512.01388v1', 'http://arxiv.org/pdf/1512.01388v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1512.01388v1', 'primary_category': 'cs.DL', 'published': '2015-12-04 12:33:07', 'summary': 'The article presents three advanced citation-based methods used to detectpotential breakthrough papers among very highly cited papers. We approach thedetection of such papers from three different perspectives in order to providedifferent typologies of breakthrough papers. In all three cases we use theclassification of scientific publications developed at CWTS based on directcitation relationships. This classification establishes clusters of papers atthree levels of aggregation. Papers are clustered based on their similarcitation orientations and it is assumed that they are focused on similarresearch interests. We use the clustering as the context for detectingpotential breakthrough papers. We utilize the Characteristics Scores and Scales(CSS) approach to partition citation distributions and implement a specificfiltering algorithm to sort out potential highly-cited followers, papers notconsidered breakthroughs in themselves. After invoking thresholds andfiltering, three methods are explored: A very exclusive one where only thehighest cited paper in a micro-cluster is considered as a potentialbreakthrough paper (M1); as well as two conceptually different methods, onethat detects potential breakthrough papers among the two percent highest citedpapers according to CSS (M2a), and finally a more restrictive version where, inaddition to the CSS two percent filter, knowledge diffusion is also taken in asan extra parameter (M2b). The advance citation-based methods are explored andevaluated using specifically validated publication sets linked to differentDanish funding instruments including centres of excellence.', 'title': 'Identifying potential breakthrough publications using refined citation analyses: Three related explorative approaches', 'updated': '2015-12-04 12:33:07'}]}]}\n", + "--------------------------------------------------\n", + "Transition number: 20\n", + "Transition type: step\n", + "Transition output: [{'result': [{'authors': ['Seng W. Loke'], 'categories': ['cs.ET', 'cs.DC'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/2208.00733v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2208.00733v1', 'http://arxiv.org/pdf/2208.00733v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2208.00733v1', 'primary_category': 'cs.ET', 'published': '2022-08-01 10:36:13', 'summary': 'This article highlights quantum Internet computing as referring todistributed quantum computing over the quantum Internet, analogous to(classical) Internet computing involving (classical) distributed computing overthe (classical) Internet. Relevant to quantum Internet computing would be areasof study such as quantum protocols for distributed nodes using quantuminformation for computations, quantum cloud computing, delegated verifiableblind or private computing, non-local gates, and distributed quantumapplications, over Internet-scale distances.', 'title': 'The Rise of Quantum Internet Computing', 'updated': '2022-08-01 10:36:13'}, {'authors': ['Seth Lloyd'], 'categories': ['quant-ph'], 'comment': '9 pages, plain TeX, article from 1998 conference proceedings\\n proposing fermionic quantum computers', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0003151v1', 'journal_ref': \"in `Unconventional Models of Computation,' C.S. Calude, J. Casti,\\n M.J. Dinneen, eds., Springer, Singapore, 1998\", 'links': ['http://arxiv.org/abs/quant-ph/0003151v1', 'http://arxiv.org/pdf/quant-ph/0003151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0003151v1', 'primary_category': 'quant-ph', 'published': '2000-03-31 22:07:23', 'summary': \"This paper investigates a variety of unconventional quantum computationdevices, including fermionic quantum computers and computers that exploitnonlinear quantum mechanics. It is shown that unconventional quantum computingdevices can in principle compute some quantities more rapidly than`conventional' quantum computers.\", 'title': 'Unconventional Quantum Computing Devices', 'updated': '2000-03-31 22:07:23'}]}, {'result': [{'authors': ['Shiqi Gong', 'Shuaiqiang Liu', 'Danny D. Sun'], 'categories': ['q-fin.PM'], 'comment': '35 pages, 7 figures, 6 tables', 'doi': None, 'entry_id': 'http://arxiv.org/abs/2306.02764v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2306.02764v1', 'http://arxiv.org/pdf/2306.02764v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2306.02764v1', 'primary_category': 'q-fin.PM', 'published': '2023-06-05 10:40:53', 'summary': \"Market making plays a crucial role in providing liquidity and maintainingstability in financial markets, making it an essential component ofwell-functioning capital markets. Despite its importance, there is limitedresearch on market making in the Chinese stock market, which is one of thelargest and most rapidly growing markets globally. To address this gap, weemploy an optimal market making framework with an exponential CARA-type(Constant Absolute Risk Aversion) utility function that accounts for variousmarket conditions, such as price drift, volatility, and stamp duty, and iscapable of describing 3 major risks (i.e., inventory, execution and adverseselection risks) in market making practice, and provide an in-depthquantitative and scenario analysis of market making in the Chinese stockmarket. Our numerical experiments explore the impact of volatility on themarket maker's inventory. Furthermore, we find that the stamp duty rate is acritical factor in market making, with a negative impact on both the profit ofthe market maker and the liquidity of the market. Additionally, our analysisemphasizes the significance of accurately estimating stock drift for managinginventory and adverse selection risks effectively and enhancing profit for themarket maker. These findings offer valuable insights for both market makers andpolicymakers in the Chinese stock market and provide directions for furtherresearch in designing effective market making strategies and policies.\", 'title': 'Optimal Market Making in the Chinese Stock Market: A Stochastic Control and Scenario Analysis', 'updated': '2023-06-05 10:40:53'}, {'authors': ['Zhongyang Zhao', 'Caisheng Wang', 'Huaiwei Liao', 'Carol J. Miller'], 'categories': ['eess.SY', 'cs.SY'], 'comment': '51st North American Power Symposium, October 2019', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1910.14515v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1910.14515v1', 'http://arxiv.org/pdf/1910.14515v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1910.14515v1', 'primary_category': 'eess.SY', 'published': '2019-10-31 14:55:25', 'summary': \"A competitive wholesale electricity market consists of thousands ofinteracting market participants. Driven by the variations of fuel costs, systemloads and weathers, these market participants compete actively and behavevariously in the power market. Although electricity markets tend to become moretransparent, a large amount of market information is still not publiclyavailable to market participants. Hence, data-driven analysis based on publicdata is crucial for market participants to better understand and modellarge-scale power markets, and ultimately to perform better in power trading.While most of the previous researches related to the large-scale power marketsare based on the synthetic networks, a data-driven approach utilizing the realpower market data is proposed in this paper. First, the power plants' monthlynet generation and capacity data are obtained from U.S. Energy InformationAdministration (EIA) and aggregated to figure out the monthly regional capacityfactors which are used to characterize the market's regional behaviors formarket participants. Then, the regional capacity factors are analyzed againstthe metered system loads and natural gas prices to study the generationbehaviors in the power market. The analysis reveals the impacts of regionalnatural gas prices on capacity factors and the responses of generatingbehaviors to the system loads. The analysis results present the solid evidenceand rational references for market participants to model and validate thelarge-scale power market in the future.\", 'title': 'Data-driven Analysis of Regional Capacity Factors in a Large-Scale Power Market: A Perspective from Market Participants', 'updated': '2019-10-31 14:55:25'}]}, {'result': [{'authors': ['Peter Lodahl'], 'categories': ['quant-ph'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1707.02094v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1707.02094v1', 'http://arxiv.org/pdf/1707.02094v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1707.02094v1', 'primary_category': 'quant-ph', 'published': '2017-07-07 09:19:15', 'summary': 'Quantum dots embedded in photonic nanostructures have in recent years provento be a very powerful solid-state platform for quantum optics experiments. Thecombination of near-unity radiative coupling of a single quantum dot to aphotonic mode and the ability to eliminate decoherence processes imply that anunprecedented light-matter interface can be obtained. As a result,high-cooperativity photon-emitter quantum interfaces can be constructed openinga path-way to deterministic photonic quantum gates for quantum-informationprocessing applications. In the present manuscript, I review currentstate-of-the-art on quantum dot devices and their applications for quantumtechnology. The overarching long-term goal of the research field is toconstruct photonic quantum networks where remote entanglement can bedistributed over long distances by photons.', 'title': 'Quantum-dot based photonic quantum networks', 'updated': '2017-07-07 09:19:15'}, {'authors': ['Ruo Peng Wang'], 'categories': ['quant-ph'], 'comment': '3 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0407030v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/quant-ph/0407030v1', 'http://arxiv.org/pdf/quant-ph/0407030v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0407030v1', 'primary_category': 'quant-ph', 'published': '2004-07-05 08:43:07', 'summary': \"I show that the photon pairs used in experimental tests of quantumnon-locality based on Bell's theorem are not in the entangled quantum state.The correct quantum state of the ``entangled'' photon pairs is suggested. Twoexperiments for testing this quantum state are proposed.\", 'title': 'Quantum State of Entangled Photon Pairs', 'updated': '2004-07-05 08:43:07'}]}, {'result': [{'authors': ['Pierre Paleo', 'Alessandro Mirone'], 'categories': ['physics.comp-ph', 'cs.CV'], 'comment': 'IUCR template, preprint mode, 35 figures', 'doi': '10.1107/S1600577515010176', 'entry_id': 'http://arxiv.org/abs/1502.01480v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1107/S1600577515010176', 'http://arxiv.org/abs/1502.01480v1', 'http://arxiv.org/pdf/1502.01480v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1502.01480v1', 'primary_category': 'physics.comp-ph', 'published': '2015-02-05 09:51:59', 'summary': 'We present a novel approach to handle ring artifacts correction in compressedsensing tomographic reconstruction. The correction is part of thereconstruction process, which differs from classical sinogram pre-processingand image post-processing techniques. The principle of compressed sensingtomographic reconstruction is presented. Then, we show that the ring artifactscorrection can be integrated in the reconstruction problem formalism. Weprovide numerical results for both simulated and real data. This technique isincluded in the PyHST2 code which is used at the European Synchrotron RadiationFacility for tomographic reconstruction.', 'title': 'Ring artifacts correction in compressed sensing tomographic reconstruction', 'updated': '2015-02-05 09:51:59'}, {'authors': ['Rasmus Dalgas Kongskov', 'Yiqiu Dong'], 'categories': ['cs.CE'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1708.06912v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1708.06912v1', 'http://arxiv.org/pdf/1708.06912v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1708.06912v1', 'primary_category': 'cs.CE', 'published': '2017-08-23 08:08:47', 'summary': 'Decomposition of tomographic reconstructions has many different practicalapplication. We propose two new reconstruction methods that combines the taskof tomographic reconstruction with object decomposition. We demonstrate thesereconstruction methods in the context of decomposing directional objects intovarious directional components. Furthermore we propose a method for estimatingthe main direction in a directional object, directly from the measured computedtomography data. We demonstrate all the proposed methods on simulated and realsamples to show their practical applicability. The numerical tests show thatdecomposition and reconstruction can combined to achieve a highly usefulfibre-crack decomposition.', 'title': 'Tomographic Reconstruction Methods for Decomposing Directional Components', 'updated': '2017-08-23 08:08:47'}]}, {'result': [{'authors': ['Liang Liu', 'Hongwei Qin', 'Jifan Hu'], 'categories': ['cond-mat.mes-hall'], 'comment': '11 pages, 8 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1904.04991v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1904.04991v1', 'http://arxiv.org/pdf/1904.04991v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1904.04991v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2019-04-10 03:37:14', 'summary': 'By the means of screened exchange density functional theory, we find that thephosphorene nanoribbons with bare zigzag edges that undergo Peierls distortionis a antiferromagnetic semiconductor in which the polarized states are mainlylocalized at the edges. Under application of external electric fields, thephosphorene nanoribbons present exotic electronic structure transitionsincluding semiconducting, half-metallic, and metallic states.', 'title': 'Exotic Electronic Property of Phosphorene Nanoribbons Driven by External Electric Field', 'updated': '2019-04-10 03:37:14'}, {'authors': ['Young Jin Kim', 'Ping-Han Chu', 'Igor Savukov', 'Shaun Newman'], 'categories': ['hep-ex', 'physics.atom-ph'], 'comment': '17 pages, 5 figures', 'doi': '10.1038/s41467-019-10169-1', 'entry_id': 'http://arxiv.org/abs/1902.00128v2', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1038/s41467-019-10169-1', 'http://arxiv.org/abs/1902.00128v2', 'http://arxiv.org/pdf/1902.00128v2'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1902.00128v2', 'primary_category': 'hep-ex', 'published': '2019-01-31 23:35:06', 'summary': 'Exotic spin-dependent interactions between fermions have recently attractedattention in relation to theories beyond the Standard Model. The exoticinteractions can be mediated by hypothetical fundamental bosons which mayexplain several unsolved mysteries in physics. Here we expand this area ofresearch by probing an exotic parity-odd spin- and velocity-dependentinteraction between the axial-vector electron coupling and the vector nucleoncoupling for polarized electrons. This experiment utilizes a high-sensitivityatomic magnetometer, based on an optically polarized vapor that is a source ofpolarized electrons, and a solid-state mass containing unpolarized nucleons.The atomic magnetometer can detect an effective magnetic field induced by theexotic interaction between unpolarized nucleons and polarized electrons. We setan experimental limit on the electron-nucleon coupling $g_\\\\text{A}^\\\\text{e}g_\\\\text{V}^\\\\text{N}<$ $10^{-30}$ at the mediator boson mass below $10^{-4}$ eV,significantly improving the current limit by up to 17 orders of magnitude.', 'title': 'Experimental limit on an exotic parity-odd spin- and velocity-dependent interaction using an optically polarized vapor', 'updated': '2019-04-24 21:40:25'}]}, {'result': [{'authors': ['R. Jackiw'], 'categories': ['hep-th'], 'comment': '13 pages, LaTeX with 2 figures (available from author), MIT-CTP #2371', 'doi': None, 'entry_id': 'http://arxiv.org/abs/hep-th/9410151v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/hep-th/9410151v1', 'http://arxiv.org/pdf/hep-th/9410151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9410151v1', 'primary_category': 'hep-th', 'published': '1994-10-20 20:21:54', 'summary': \"25th anniversary and new building dedication Centre de RecherchesMath\\\\'{e}matiques Montr\\\\'{e}al, Canada, October 1994\", 'title': 'My Encounters --- as a Physicist --- with Mathematics', 'updated': '1994-10-20 20:21:54'}, {'authors': ['R. Jackiw'], 'categories': ['hep-th', 'hep-ph', 'physics.hist-ph', 'quant-ph'], 'comment': 'Email correspondence to jackiw@mitlns.mit.edu ; 4 pages, LaTeX', 'doi': '10.1073/pnas.95.22.12776', 'entry_id': 'http://arxiv.org/abs/hep-th/9709212v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1073/pnas.95.22.12776', 'http://arxiv.org/abs/hep-th/9709212v1', 'http://arxiv.org/pdf/hep-th/9709212v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9709212v1', 'primary_category': 'hep-th', 'published': '1997-09-29 20:50:54', 'summary': 'The present-day crisis in quantum field theory is described.', 'title': 'What is quantum field theory and why have some physicists abandoned it?', 'updated': '1997-09-29 20:50:54'}]}, {'result': [{'authors': ['Maximilian Russ', 'Guido Burkard'], 'categories': ['cond-mat.mes-hall'], 'comment': '42 pages, 13 figures', 'doi': '10.1088/1361-648X/aa761f', 'entry_id': 'http://arxiv.org/abs/1611.09106v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1088/1361-648X/aa761f', 'http://arxiv.org/abs/1611.09106v1', 'http://arxiv.org/pdf/1611.09106v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.09106v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2016-11-28 13:21:43', 'summary': 'The goal of this article is to review the progress of three-electron spinqubits from their inception to the state of the art. We direct the main focustowards the resonant exchange (RX) qubit and the exchange-only qubit, but wealso discuss other qubit implementations using three electron spins. For eachthree-spin qubit we describe the qubit model, the physical realization, theimplementations of single-qubit operations, as well as the read-out andinitialization schemes. Two-qubit gates and decoherence properties arediscussed for the RX qubit and the exchange-only qubit, thereby, completing thelist of requirements for a viable candidate qubit implementation for quantumcomputation. We start with describing the full system of three electrons in atriple quantum dot, then discuss the charge-stability diagram and restrictourselves to the relevant subsystem, introduce the qubit states, and discussimportant transitions to other charge states. Introducing the various qubitimplementations, we begin with the exchange-only qubit, followed by thespin-charge qubit, the hybrid qubit, and the RX qubit, discussing for each thesingle-qubit operations, read-out, and initialization methods, whereas the mainfocus will be on the RX qubit, whose single-qubit operations are realized bydriving the qubit at its resonant frequency in the microwave range similar toelectron spin resonance. Two different types of two-qubit operations arepresented for the exchange-only and the RX qubit which can be divided intoshort-ranged and long-ranged interactions. Both of these interaction types canbe expected to be necessary in a large-scale quantum computer. We also takeinto account the decoherence of the qubit through the influence of magneticnoise as well as dephasing due to charge noise.', 'title': 'Three-electron spin qubits', 'updated': '2016-11-28 13:21:43'}, {'authors': ['Sam Young Cho', 'Mun Dae Kim'], 'categories': ['cond-mat.supr-con', 'cond-mat.mes-hall'], 'comment': '5 pages, 1 figure', 'doi': '10.1103/PhysRevB.77.212506', 'entry_id': 'http://arxiv.org/abs/cond-mat/0703505v1', 'journal_ref': 'Phys. Rev. B 77, 212506 (2008)', 'links': ['http://dx.doi.org/10.1103/PhysRevB.77.212506', 'http://arxiv.org/abs/cond-mat/0703505v1', 'http://arxiv.org/pdf/cond-mat/0703505v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/cond-mat/0703505v1', 'primary_category': 'cond-mat.supr-con', 'published': '2007-03-20 02:20:35', 'summary': 'Superconducting flux qubits are considered to investigate macroscopicmany-qubit interactions. Many-qubit states based on current states can be manipulated through thecurrent-phase relation in each superconducting loop. For flux qubit systems comprised of $N$ qubit loops, a general expression oflow energy Hamiltonian is presented in terms of low energy levels of qubits andmacroscopic quantum tunnelings between the many-qubit states. Many-qubit interactions classified by {\\\\em Ising type- or tunnel-}exchangeinteractions can be observable experimentally. Flux qubit systems can provide various artificial-spin systems to studymany-body systems that cannot be found naturally.', 'title': 'Macroscopic Many-Qubit Interactions in Superconducting Flux Qubits', 'updated': '2007-03-20 02:20:35'}]}, {'result': [{'authors': ['Henry Cohn'], 'categories': ['math.MG', 'math.NT'], 'comment': '24 pages, 9 figures', 'doi': '10.1090/noti1474', 'entry_id': 'http://arxiv.org/abs/1611.01685v1', 'journal_ref': 'Notices Amer. Math. Soc. 64 (2017), no. 2, 102-115', 'links': ['http://dx.doi.org/10.1090/noti1474', 'http://arxiv.org/abs/1611.01685v1', 'http://arxiv.org/pdf/1611.01685v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.01685v1', 'primary_category': 'math.MG', 'published': '2016-11-05 18:16:28', 'summary': \"This expository paper describes Viazovska's breakthrough solution of thesphere packing problem in eight dimensions, as well as its extension totwenty-four dimensions by Cohn, Kumar, Miller, Radchenko, and Viazovska.\", 'title': 'A conceptual breakthrough in sphere packing', 'updated': '2016-11-05 18:16:28'}, {'authors': ['Jesper W. Schneider', 'Rodrigo Costas'], 'categories': ['cs.DL'], 'comment': 'Accepted for publication in Journal of the Association for\\n Information Science and Technology', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1512.01388v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1512.01388v1', 'http://arxiv.org/pdf/1512.01388v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1512.01388v1', 'primary_category': 'cs.DL', 'published': '2015-12-04 12:33:07', 'summary': 'The article presents three advanced citation-based methods used to detectpotential breakthrough papers among very highly cited papers. We approach thedetection of such papers from three different perspectives in order to providedifferent typologies of breakthrough papers. In all three cases we use theclassification of scientific publications developed at CWTS based on directcitation relationships. This classification establishes clusters of papers atthree levels of aggregation. Papers are clustered based on their similarcitation orientations and it is assumed that they are focused on similarresearch interests. We use the clustering as the context for detectingpotential breakthrough papers. We utilize the Characteristics Scores and Scales(CSS) approach to partition citation distributions and implement a specificfiltering algorithm to sort out potential highly-cited followers, papers notconsidered breakthroughs in themselves. After invoking thresholds andfiltering, three methods are explored: A very exclusive one where only thehighest cited paper in a micro-cluster is considered as a potentialbreakthrough paper (M1); as well as two conceptually different methods, onethat detects potential breakthrough papers among the two percent highest citedpapers according to CSS (M2a), and finally a more restrictive version where, inaddition to the CSS two percent filter, knowledge diffusion is also taken in asan extra parameter (M2b). The advance citation-based methods are explored andevaluated using specifically validated publication sets linked to differentDanish funding instruments including centres of excellence.', 'title': 'Identifying potential breakthrough publications using refined citation analyses: Three related explorative approaches', 'updated': '2015-12-04 12:33:07'}]}]\n", + "--------------------------------------------------\n", + "Transition number: 21\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Maximilian Russ', 'Guido Burkard'], 'categories': ['cond-mat.mes-hall'], 'comment': '42 pages, 13 figures', 'doi': '10.1088/1361-648X/aa761f', 'entry_id': 'http://arxiv.org/abs/1611.09106v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1088/1361-648X/aa761f', 'http://arxiv.org/abs/1611.09106v1', 'http://arxiv.org/pdf/1611.09106v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.09106v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2016-11-28 13:21:43', 'summary': 'The goal of this article is to review the progress of three-electron spinqubits from their inception to the state of the art. We direct the main focustowards the resonant exchange (RX) qubit and the exchange-only qubit, but wealso discuss other qubit implementations using three electron spins. For eachthree-spin qubit we describe the qubit model, the physical realization, theimplementations of single-qubit operations, as well as the read-out andinitialization schemes. Two-qubit gates and decoherence properties arediscussed for the RX qubit and the exchange-only qubit, thereby, completing thelist of requirements for a viable candidate qubit implementation for quantumcomputation. We start with describing the full system of three electrons in atriple quantum dot, then discuss the charge-stability diagram and restrictourselves to the relevant subsystem, introduce the qubit states, and discussimportant transitions to other charge states. Introducing the various qubitimplementations, we begin with the exchange-only qubit, followed by thespin-charge qubit, the hybrid qubit, and the RX qubit, discussing for each thesingle-qubit operations, read-out, and initialization methods, whereas the mainfocus will be on the RX qubit, whose single-qubit operations are realized bydriving the qubit at its resonant frequency in the microwave range similar toelectron spin resonance. Two different types of two-qubit operations arepresented for the exchange-only and the RX qubit which can be divided intoshort-ranged and long-ranged interactions. Both of these interaction types canbe expected to be necessary in a large-scale quantum computer. We also takeinto account the decoherence of the qubit through the influence of magneticnoise as well as dephasing due to charge noise.', 'title': 'Three-electron spin qubits', 'updated': '2016-11-28 13:21:43'}, {'authors': ['Sam Young Cho', 'Mun Dae Kim'], 'categories': ['cond-mat.supr-con', 'cond-mat.mes-hall'], 'comment': '5 pages, 1 figure', 'doi': '10.1103/PhysRevB.77.212506', 'entry_id': 'http://arxiv.org/abs/cond-mat/0703505v1', 'journal_ref': 'Phys. Rev. B 77, 212506 (2008)', 'links': ['http://dx.doi.org/10.1103/PhysRevB.77.212506', 'http://arxiv.org/abs/cond-mat/0703505v1', 'http://arxiv.org/pdf/cond-mat/0703505v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/cond-mat/0703505v1', 'primary_category': 'cond-mat.supr-con', 'published': '2007-03-20 02:20:35', 'summary': 'Superconducting flux qubits are considered to investigate macroscopicmany-qubit interactions. Many-qubit states based on current states can be manipulated through thecurrent-phase relation in each superconducting loop. For flux qubit systems comprised of $N$ qubit loops, a general expression oflow energy Hamiltonian is presented in terms of low energy levels of qubits andmacroscopic quantum tunnelings between the many-qubit states. Many-qubit interactions classified by {\\\\em Ising type- or tunnel-}exchangeinteractions can be observable experimentally. Flux qubit systems can provide various artificial-spin systems to studymany-body systems that cannot be found naturally.', 'title': 'Macroscopic Many-Qubit Interactions in Superconducting Flux Qubits', 'updated': '2007-03-20 02:20:35'}]}\n", + "--------------------------------------------------\n", + "Transition number: 22\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['R. Jackiw'], 'categories': ['hep-th'], 'comment': '13 pages, LaTeX with 2 figures (available from author), MIT-CTP #2371', 'doi': None, 'entry_id': 'http://arxiv.org/abs/hep-th/9410151v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/hep-th/9410151v1', 'http://arxiv.org/pdf/hep-th/9410151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9410151v1', 'primary_category': 'hep-th', 'published': '1994-10-20 20:21:54', 'summary': \"25th anniversary and new building dedication Centre de RecherchesMath\\\\'{e}matiques Montr\\\\'{e}al, Canada, October 1994\", 'title': 'My Encounters --- as a Physicist --- with Mathematics', 'updated': '1994-10-20 20:21:54'}, {'authors': ['R. Jackiw'], 'categories': ['hep-th', 'hep-ph', 'physics.hist-ph', 'quant-ph'], 'comment': 'Email correspondence to jackiw@mitlns.mit.edu ; 4 pages, LaTeX', 'doi': '10.1073/pnas.95.22.12776', 'entry_id': 'http://arxiv.org/abs/hep-th/9709212v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1073/pnas.95.22.12776', 'http://arxiv.org/abs/hep-th/9709212v1', 'http://arxiv.org/pdf/hep-th/9709212v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/hep-th/9709212v1', 'primary_category': 'hep-th', 'published': '1997-09-29 20:50:54', 'summary': 'The present-day crisis in quantum field theory is described.', 'title': 'What is quantum field theory and why have some physicists abandoned it?', 'updated': '1997-09-29 20:50:54'}]}\n", + "--------------------------------------------------\n", + "Transition number: 23\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Liang Liu', 'Hongwei Qin', 'Jifan Hu'], 'categories': ['cond-mat.mes-hall'], 'comment': '11 pages, 8 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1904.04991v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1904.04991v1', 'http://arxiv.org/pdf/1904.04991v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1904.04991v1', 'primary_category': 'cond-mat.mes-hall', 'published': '2019-04-10 03:37:14', 'summary': 'By the means of screened exchange density functional theory, we find that thephosphorene nanoribbons with bare zigzag edges that undergo Peierls distortionis a antiferromagnetic semiconductor in which the polarized states are mainlylocalized at the edges. Under application of external electric fields, thephosphorene nanoribbons present exotic electronic structure transitionsincluding semiconducting, half-metallic, and metallic states.', 'title': 'Exotic Electronic Property of Phosphorene Nanoribbons Driven by External Electric Field', 'updated': '2019-04-10 03:37:14'}, {'authors': ['Young Jin Kim', 'Ping-Han Chu', 'Igor Savukov', 'Shaun Newman'], 'categories': ['hep-ex', 'physics.atom-ph'], 'comment': '17 pages, 5 figures', 'doi': '10.1038/s41467-019-10169-1', 'entry_id': 'http://arxiv.org/abs/1902.00128v2', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1038/s41467-019-10169-1', 'http://arxiv.org/abs/1902.00128v2', 'http://arxiv.org/pdf/1902.00128v2'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1902.00128v2', 'primary_category': 'hep-ex', 'published': '2019-01-31 23:35:06', 'summary': 'Exotic spin-dependent interactions between fermions have recently attractedattention in relation to theories beyond the Standard Model. The exoticinteractions can be mediated by hypothetical fundamental bosons which mayexplain several unsolved mysteries in physics. Here we expand this area ofresearch by probing an exotic parity-odd spin- and velocity-dependentinteraction between the axial-vector electron coupling and the vector nucleoncoupling for polarized electrons. This experiment utilizes a high-sensitivityatomic magnetometer, based on an optically polarized vapor that is a source ofpolarized electrons, and a solid-state mass containing unpolarized nucleons.The atomic magnetometer can detect an effective magnetic field induced by theexotic interaction between unpolarized nucleons and polarized electrons. We setan experimental limit on the electron-nucleon coupling $g_\\\\text{A}^\\\\text{e}g_\\\\text{V}^\\\\text{N}<$ $10^{-30}$ at the mediator boson mass below $10^{-4}$ eV,significantly improving the current limit by up to 17 orders of magnitude.', 'title': 'Experimental limit on an exotic parity-odd spin- and velocity-dependent interaction using an optically polarized vapor', 'updated': '2019-04-24 21:40:25'}]}\n", + "--------------------------------------------------\n", + "Transition number: 24\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Henry Cohn'], 'categories': ['math.MG', 'math.NT'], 'comment': '24 pages, 9 figures', 'doi': '10.1090/noti1474', 'entry_id': 'http://arxiv.org/abs/1611.01685v1', 'journal_ref': 'Notices Amer. Math. Soc. 64 (2017), no. 2, 102-115', 'links': ['http://dx.doi.org/10.1090/noti1474', 'http://arxiv.org/abs/1611.01685v1', 'http://arxiv.org/pdf/1611.01685v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1611.01685v1', 'primary_category': 'math.MG', 'published': '2016-11-05 18:16:28', 'summary': \"This expository paper describes Viazovska's breakthrough solution of thesphere packing problem in eight dimensions, as well as its extension totwenty-four dimensions by Cohn, Kumar, Miller, Radchenko, and Viazovska.\", 'title': 'A conceptual breakthrough in sphere packing', 'updated': '2016-11-05 18:16:28'}, {'authors': ['Jesper W. Schneider', 'Rodrigo Costas'], 'categories': ['cs.DL'], 'comment': 'Accepted for publication in Journal of the Association for\\n Information Science and Technology', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1512.01388v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1512.01388v1', 'http://arxiv.org/pdf/1512.01388v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1512.01388v1', 'primary_category': 'cs.DL', 'published': '2015-12-04 12:33:07', 'summary': 'The article presents three advanced citation-based methods used to detectpotential breakthrough papers among very highly cited papers. We approach thedetection of such papers from three different perspectives in order to providedifferent typologies of breakthrough papers. In all three cases we use theclassification of scientific publications developed at CWTS based on directcitation relationships. This classification establishes clusters of papers atthree levels of aggregation. Papers are clustered based on their similarcitation orientations and it is assumed that they are focused on similarresearch interests. We use the clustering as the context for detectingpotential breakthrough papers. We utilize the Characteristics Scores and Scales(CSS) approach to partition citation distributions and implement a specificfiltering algorithm to sort out potential highly-cited followers, papers notconsidered breakthroughs in themselves. After invoking thresholds andfiltering, three methods are explored: A very exclusive one where only thehighest cited paper in a micro-cluster is considered as a potentialbreakthrough paper (M1); as well as two conceptually different methods, onethat detects potential breakthrough papers among the two percent highest citedpapers according to CSS (M2a), and finally a more restrictive version where, inaddition to the CSS two percent filter, knowledge diffusion is also taken in asan extra parameter (M2b). The advance citation-based methods are explored andevaluated using specifically validated publication sets linked to differentDanish funding instruments including centres of excellence.', 'title': 'Identifying potential breakthrough publications using refined citation analyses: Three related explorative approaches', 'updated': '2015-12-04 12:33:07'}]}\n", + "--------------------------------------------------\n", + "Transition number: 25\n", + "Transition type: init_branch\n", + "Transition output: MIT Physicists\n", + "--------------------------------------------------\n", + "Transition number: 26\n", + "Transition type: init_branch\n", + "Transition output: Qubits\n", + "--------------------------------------------------\n", + "Transition number: 27\n", + "Transition type: init_branch\n", + "Transition output: Exotic Electronic State\n", + "--------------------------------------------------\n", + "Transition number: 28\n", + "Transition type: init_branch\n", + "Transition output: Breakthroughs\n", + "--------------------------------------------------\n", + "Transition number: 29\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Peter Lodahl'], 'categories': ['quant-ph'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1707.02094v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1707.02094v1', 'http://arxiv.org/pdf/1707.02094v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1707.02094v1', 'primary_category': 'quant-ph', 'published': '2017-07-07 09:19:15', 'summary': 'Quantum dots embedded in photonic nanostructures have in recent years provento be a very powerful solid-state platform for quantum optics experiments. Thecombination of near-unity radiative coupling of a single quantum dot to aphotonic mode and the ability to eliminate decoherence processes imply that anunprecedented light-matter interface can be obtained. As a result,high-cooperativity photon-emitter quantum interfaces can be constructed openinga path-way to deterministic photonic quantum gates for quantum-informationprocessing applications. In the present manuscript, I review currentstate-of-the-art on quantum dot devices and their applications for quantumtechnology. The overarching long-term goal of the research field is toconstruct photonic quantum networks where remote entanglement can bedistributed over long distances by photons.', 'title': 'Quantum-dot based photonic quantum networks', 'updated': '2017-07-07 09:19:15'}, {'authors': ['Ruo Peng Wang'], 'categories': ['quant-ph'], 'comment': '3 figures', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0407030v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/quant-ph/0407030v1', 'http://arxiv.org/pdf/quant-ph/0407030v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0407030v1', 'primary_category': 'quant-ph', 'published': '2004-07-05 08:43:07', 'summary': \"I show that the photon pairs used in experimental tests of quantumnon-locality based on Bell's theorem are not in the entangled quantum state.The correct quantum state of the ``entangled'' photon pairs is suggested. Twoexperiments for testing this quantum state are proposed.\", 'title': 'Quantum State of Entangled Photon Pairs', 'updated': '2004-07-05 08:43:07'}]}\n", + "--------------------------------------------------\n", + "Transition number: 30\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Pierre Paleo', 'Alessandro Mirone'], 'categories': ['physics.comp-ph', 'cs.CV'], 'comment': 'IUCR template, preprint mode, 35 figures', 'doi': '10.1107/S1600577515010176', 'entry_id': 'http://arxiv.org/abs/1502.01480v1', 'journal_ref': None, 'links': ['http://dx.doi.org/10.1107/S1600577515010176', 'http://arxiv.org/abs/1502.01480v1', 'http://arxiv.org/pdf/1502.01480v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1502.01480v1', 'primary_category': 'physics.comp-ph', 'published': '2015-02-05 09:51:59', 'summary': 'We present a novel approach to handle ring artifacts correction in compressedsensing tomographic reconstruction. The correction is part of thereconstruction process, which differs from classical sinogram pre-processingand image post-processing techniques. The principle of compressed sensingtomographic reconstruction is presented. Then, we show that the ring artifactscorrection can be integrated in the reconstruction problem formalism. Weprovide numerical results for both simulated and real data. This technique isincluded in the PyHST2 code which is used at the European Synchrotron RadiationFacility for tomographic reconstruction.', 'title': 'Ring artifacts correction in compressed sensing tomographic reconstruction', 'updated': '2015-02-05 09:51:59'}, {'authors': ['Rasmus Dalgas Kongskov', 'Yiqiu Dong'], 'categories': ['cs.CE'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/1708.06912v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1708.06912v1', 'http://arxiv.org/pdf/1708.06912v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1708.06912v1', 'primary_category': 'cs.CE', 'published': '2017-08-23 08:08:47', 'summary': 'Decomposition of tomographic reconstructions has many different practicalapplication. We propose two new reconstruction methods that combines the taskof tomographic reconstruction with object decomposition. We demonstrate thesereconstruction methods in the context of decomposing directional objects intovarious directional components. Furthermore we propose a method for estimatingthe main direction in a directional object, directly from the measured computedtomography data. We demonstrate all the proposed methods on simulated and realsamples to show their practical applicability. The numerical tests show thatdecomposition and reconstruction can combined to achieve a highly usefulfibre-crack decomposition.', 'title': 'Tomographic Reconstruction Methods for Decomposing Directional Components', 'updated': '2017-08-23 08:08:47'}]}\n", + "--------------------------------------------------\n", + "Transition number: 31\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Seng W. Loke'], 'categories': ['cs.ET', 'cs.DC'], 'comment': None, 'doi': None, 'entry_id': 'http://arxiv.org/abs/2208.00733v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2208.00733v1', 'http://arxiv.org/pdf/2208.00733v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2208.00733v1', 'primary_category': 'cs.ET', 'published': '2022-08-01 10:36:13', 'summary': 'This article highlights quantum Internet computing as referring todistributed quantum computing over the quantum Internet, analogous to(classical) Internet computing involving (classical) distributed computing overthe (classical) Internet. Relevant to quantum Internet computing would be areasof study such as quantum protocols for distributed nodes using quantuminformation for computations, quantum cloud computing, delegated verifiableblind or private computing, non-local gates, and distributed quantumapplications, over Internet-scale distances.', 'title': 'The Rise of Quantum Internet Computing', 'updated': '2022-08-01 10:36:13'}, {'authors': ['Seth Lloyd'], 'categories': ['quant-ph'], 'comment': '9 pages, plain TeX, article from 1998 conference proceedings\\n proposing fermionic quantum computers', 'doi': None, 'entry_id': 'http://arxiv.org/abs/quant-ph/0003151v1', 'journal_ref': \"in `Unconventional Models of Computation,' C.S. Calude, J. Casti,\\n M.J. Dinneen, eds., Springer, Singapore, 1998\", 'links': ['http://arxiv.org/abs/quant-ph/0003151v1', 'http://arxiv.org/pdf/quant-ph/0003151v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0003151v1', 'primary_category': 'quant-ph', 'published': '2000-03-31 22:07:23', 'summary': \"This paper investigates a variety of unconventional quantum computationdevices, including fermionic quantum computers and computers that exploitnonlinear quantum mechanics. It is shown that unconventional quantum computingdevices can in principle compute some quantities more rapidly than`conventional' quantum computers.\", 'title': 'Unconventional Quantum Computing Devices', 'updated': '2000-03-31 22:07:23'}]}\n", + "--------------------------------------------------\n", + "Transition number: 32\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'authors': ['Shiqi Gong', 'Shuaiqiang Liu', 'Danny D. Sun'], 'categories': ['q-fin.PM'], 'comment': '35 pages, 7 figures, 6 tables', 'doi': None, 'entry_id': 'http://arxiv.org/abs/2306.02764v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/2306.02764v1', 'http://arxiv.org/pdf/2306.02764v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/2306.02764v1', 'primary_category': 'q-fin.PM', 'published': '2023-06-05 10:40:53', 'summary': \"Market making plays a crucial role in providing liquidity and maintainingstability in financial markets, making it an essential component ofwell-functioning capital markets. Despite its importance, there is limitedresearch on market making in the Chinese stock market, which is one of thelargest and most rapidly growing markets globally. To address this gap, weemploy an optimal market making framework with an exponential CARA-type(Constant Absolute Risk Aversion) utility function that accounts for variousmarket conditions, such as price drift, volatility, and stamp duty, and iscapable of describing 3 major risks (i.e., inventory, execution and adverseselection risks) in market making practice, and provide an in-depthquantitative and scenario analysis of market making in the Chinese stockmarket. Our numerical experiments explore the impact of volatility on themarket maker's inventory. Furthermore, we find that the stamp duty rate is acritical factor in market making, with a negative impact on both the profit ofthe market maker and the liquidity of the market. Additionally, our analysisemphasizes the significance of accurately estimating stock drift for managinginventory and adverse selection risks effectively and enhancing profit for themarket maker. These findings offer valuable insights for both market makers andpolicymakers in the Chinese stock market and provide directions for furtherresearch in designing effective market making strategies and policies.\", 'title': 'Optimal Market Making in the Chinese Stock Market: A Stochastic Control and Scenario Analysis', 'updated': '2023-06-05 10:40:53'}, {'authors': ['Zhongyang Zhao', 'Caisheng Wang', 'Huaiwei Liao', 'Carol J. Miller'], 'categories': ['eess.SY', 'cs.SY'], 'comment': '51st North American Power Symposium, October 2019', 'doi': None, 'entry_id': 'http://arxiv.org/abs/1910.14515v1', 'journal_ref': None, 'links': ['http://arxiv.org/abs/1910.14515v1', 'http://arxiv.org/pdf/1910.14515v1'], 'pdf_downloaded': None, 'pdf_url': 'http://arxiv.org/pdf/1910.14515v1', 'primary_category': 'eess.SY', 'published': '2019-10-31 14:55:25', 'summary': \"A competitive wholesale electricity market consists of thousands ofinteracting market participants. Driven by the variations of fuel costs, systemloads and weathers, these market participants compete actively and behavevariously in the power market. Although electricity markets tend to become moretransparent, a large amount of market information is still not publiclyavailable to market participants. Hence, data-driven analysis based on publicdata is crucial for market participants to better understand and modellarge-scale power markets, and ultimately to perform better in power trading.While most of the previous researches related to the large-scale power marketsare based on the synthetic networks, a data-driven approach utilizing the realpower market data is proposed in this paper. First, the power plants' monthlynet generation and capacity data are obtained from U.S. Energy InformationAdministration (EIA) and aggregated to figure out the monthly regional capacityfactors which are used to characterize the market's regional behaviors formarket participants. Then, the regional capacity factors are analyzed againstthe metered system loads and natural gas prices to study the generationbehaviors in the power market. The analysis reveals the impacts of regionalnatural gas prices on capacity factors and the responses of generatingbehaviors to the system loads. The analysis results present the solid evidenceand rational references for market participants to model and validate thelarge-scale power market in the future.\", 'title': 'Data-driven Analysis of Regional Capacity Factors in a Large-Scale Power Market: A Perspective from Market Participants', 'updated': '2019-10-31 14:55:25'}]}\n", + "--------------------------------------------------\n", + "Transition number: 33\n", + "Transition type: init_branch\n", + "Transition output: Quantum Photonics Experiment\n", + "--------------------------------------------------\n", + "Transition number: 34\n", + "Transition type: init_branch\n", + "Transition output: Market Research\n", + "--------------------------------------------------\n", + "Transition number: 35\n", + "Transition type: init_branch\n", + "Transition output: Quantum Computing\n", + "--------------------------------------------------\n", + "Transition number: 36\n", + "Transition type: init_branch\n", + "Transition output: Tomographic Reconstruction\n", + "--------------------------------------------------\n", + "Transition number: 37\n", + "Transition type: step\n", + "Transition output: {'keywords': ['Quantum Computing', ' Market Research', ' Quantum Photonics Experiment', ' Tomographic Reconstruction', ' Exotic Electronic State', ' MIT Physicists', ' Qubits', ' Breakthroughs']}\n", + "--------------------------------------------------\n", + "Transition number: 38\n", + "Transition type: step\n", + "Transition output: Quantum Computing, Market Research, Quantum Photonics Experiment, Tomographic Reconstruction, Exotic Electronic State, MIT Physicists, Qubits, Breakthroughs, Major Acquisitions, Investments, AI Safety, China, LLM, Large Language Models, Graduate Salary Figures\n", + "--------------------------------------------------\n", + "Transition number: 39\n", + "Transition type: step\n", + "Transition output: [{'result': [{'link': 'https://thequantuminsider.com/', 'snippet': 'Find the latest Quantum Computing news, data, market research, and insights. To stay up to date with the quantum market click here!', 'title': 'Quantum Computing News & Top Stories | The Quantum Insider'}, {'link': 'https://www.sciencedaily.com/news/computers_math/quantum_computers/', 'snippet': 'Oct. 24, 2024 \\x97 Scientists have used high-performance computing at large scales to analyze a quantum photonics experiment. In specific terms, this involved the tomographic reconstruction of experimental data from a ...', 'title': 'Quantum Computers News -- ScienceDaily'}, {'link': 'https://news.mit.edu/topic/quantum-computing', 'snippet': 'An exotic electronic state observed by MIT physicists could enable more robust forms of quantum computing. ... The method lets researchers identify and control larger numbers of atomic-scale defects, to build a bigger system of qubits.', 'title': 'Quantum computing | MIT News | Massachusetts Institute of Technology'}]}, {'result': [{'link': 'https://www.startus-insights.com/innovators-guide/llm-news-brief/', 'snippet': 'You can catch up on the latest, must-know breakthroughs, major acquisitons & investments, and other events in the LLM landscape, covering everything from the growing focus on AI safety to China's acceptance of 14 LLMs for public release. LLM NEWSLLMSLLM NEWS BRIEFLARGE LANGUAGE MODELSQ2 2024LARGE ...', 'title': \"What's Currently Happening in LLMs? | Q2 2024\"}, {'link': 'https://llm-guide.com/news', 'snippet': 'The class of 2023 is doing very well, indeed! For the fifth year in a row, graduate salary figures have reached another all-time high.', 'title': 'LLM News | LLM GUIDE'}, {'link': 'https://www.reddit.com/r/LocalLLaMA/comments/18rjv8c/are_there_any_llms_that_are_frequently_updated/', 'snippet': 'Is there anything like that? If I wanted to chat with an LLM that would know about last week's major happenings.. is there one that's constantly…', 'title': 'r/LocalLLaMA on Reddit: Are there any LLMs that are frequently updated with the latest news and events of the world?'}]}]\n", + "--------------------------------------------------\n", + "Transition number: 40\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://thequantuminsider.com/', 'snippet': 'Find the latest Quantum Computing news, data, market research, and insights. To stay up to date with the quantum market click here!', 'title': 'Quantum Computing News & Top Stories | The Quantum Insider'}, {'link': 'https://www.sciencedaily.com/news/computers_math/quantum_computers/', 'snippet': 'Oct. 24, 2024 \\x97 Scientists have used high-performance computing at large scales to analyze a quantum photonics experiment. In specific terms, this involved the tomographic reconstruction of experimental data from a ...', 'title': 'Quantum Computers News -- ScienceDaily'}, {'link': 'https://news.mit.edu/topic/quantum-computing', 'snippet': 'An exotic electronic state observed by MIT physicists could enable more robust forms of quantum computing. ... The method lets researchers identify and control larger numbers of atomic-scale defects, to build a bigger system of qubits.', 'title': 'Quantum computing | MIT News | Massachusetts Institute of Technology'}]}\n", + "--------------------------------------------------\n", + "Transition number: 41\n", + "Transition type: finish_branch\n", + "Transition output: {'result': [{'link': 'https://www.startus-insights.com/innovators-guide/llm-news-brief/', 'snippet': 'You can catch up on the latest, must-know breakthroughs, major acquisitons & investments, and other events in the LLM landscape, covering everything from the growing focus on AI safety to China's acceptance of 14 LLMs for public release. LLM NEWSLLMSLLM NEWS BRIEFLARGE LANGUAGE MODELSQ2 2024LARGE ...', 'title': \"What's Currently Happening in LLMs? | Q2 2024\"}, {'link': 'https://llm-guide.com/news', 'snippet': 'The class of 2023 is doing very well, indeed! For the fifth year in a row, graduate salary figures have reached another all-time high.', 'title': 'LLM News | LLM GUIDE'}, {'link': 'https://www.reddit.com/r/LocalLLaMA/comments/18rjv8c/are_there_any_llms_that_are_frequently_updated/', 'snippet': 'Is there anything like that? If I wanted to chat with an LLM that would know about last week's major happenings.. is there one that's constantly…', 'title': 'r/LocalLLaMA on Reddit: Are there any LLMs that are frequently updated with the latest news and events of the world?'}]}\n", + "--------------------------------------------------\n", + "Transition number: 42\n", + "Transition type: init_branch\n", + "Transition output: LLMs\n", + "--------------------------------------------------\n", + "Transition number: 43\n", + "Transition type: init_branch\n", + "Transition output: Quantum Computing\n", + "--------------------------------------------------\n", + "Transition number: 44\n", + "Transition type: init\n", + "Transition output: {'topics': ['Quantum Computing', 'LLMs'], 'user_id': '70ba3a2c-b5ab-4074-8f01-fe9f81771c17'}\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# Lists all the task steps that have been executed up to this point in time\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items\n", + "\n", + "# Transitions are retreived in chronological order that is the latest transition is at the start of the list\n", + "for index, transition in enumerate(transitions):\n", + " print(\"Transition number: \", index)\n", + " print(\"Transition type: \", transition.type)\n", + " print(\"Transition output: \", transition.output)\n", + " print(\"-\"*50)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'created_at': '2024-11-28T20:15:42.025043Z',\n", + " 'id': 'c9932fdf-6ec7-449b-af0d-36ec80a22f73',\n", + " 'jobs': ['0ffe21ef-a536-43b2-a948-9766146dd6e5']}\n" + ] + } + ], + "source": [ + "import pprint\n", + "# Retrieves the output of the last transition that took place\n", + "output = client.executions.transitions.list(execution_id=execution.id).items[0].output\n", + "# Pretty printing the output\n", + "pprint.pprint(output)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'paper_titles_and_urls': [['The Rise of Quantum Internet Computing',\n", + " 'https://arxiv.org/pdf/2208.00733v1'],\n", + " ['Unconventional Quantum Computing Devices',\n", + " 'https://arxiv.org/pdf/quant-ph/0003151v1'],\n", + " ['Quantum-dot based photonic quantum networks',\n", + " 'https://arxiv.org/pdf/1707.02094v1'],\n", + " ['Quantum State of Entangled Photon Pairs',\n", + " 'https://arxiv.org/pdf/quant-ph/0407030v1'],\n", + " ['Three-electron spin qubits',\n", + " 'https://arxiv.org/pdf/1611.09106v1']]}\n" + ] + } + ], + "source": [ + "# top 5 research papers selected by the agent based on the user persona\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items[13]\n", + "# pretty printing the research papers\n", + "pprint.pprint(transitions.output)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'paper_contents': \"[{'documents': []}, {'documents': [{'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'5dde80d8-9fea-4e79-a8c6-4f56f6989bc5', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'arXiv:quant-ph/0003151v1 \"\n", + " '31 Mar 2000\\\\n\\\\n '\n", + " 'Unconventional Quantum Computing '\n", + " 'Devices\\\\n\\\\n '\n", + " 'Seth '\n", + " 'Lloyd\\\\n '\n", + " 'Mechanical '\n", + " 'Engineering\\\\n '\n", + " 'MIT '\n", + " '3-160\\\\n '\n", + " 'Cambridge, Mass. 02139\\\\n\\\\n Abstract: '\n", + " 'This paper investigates a variety of unconventional '\n", + " 'quantum computation de-\\\\n vices, '\n", + " 'including fermionic quantum computers and computers that '\n", + " 'exploit nonlinear quan-\\\\n tum mechanics. '\n", + " 'It is shown that unconventional quantum computing devices '\n", + " 'can in prin-\\\\n ciple compute some '\n", + " 'quantities more rapidly than ‘conventional’ quantum '\n", + " 'computers.\\\\n\\\\n Computers are '\n", + " 'physical: what they can and cannot do is determined by the '\n", + " 'laws\\\\n of physics. When scientific '\n", + " 'progress augments or revises those laws, our picture of '\n", + " 'what\\\\n computers can do changes. '\n", + " 'Currently, quantum mechanics is generally accepted as '\n", + " 'the\\\\n fundamental dynamical theory of how '\n", + " 'physical systems behave. Quantum computers '\n", + " 'can\\\\n in principle exploit quantum '\n", + " 'coherence to perform computational tasks that classical '\n", + " 'com-\\\\n puters cannot [1-21]. If someday '\n", + " 'quantum mechanics should turn out to be '\n", + " 'incomplete\\\\n or faulty, then our picture '\n", + " 'of what computers can do will change. In addition, the '\n", + " 'set\\\\n of known quantum phenomena is '\n", + " 'constantly increasing: essentially any coherent '\n", + " 'quantum\\\\n phenomenon involving nonlinear '\n", + " 'interactions between quantum degrees of freedom '\n", + " 'can\\\\n in principle be exploited to '\n", + " 'perform quantum logic. This paper discusses how the '\n", + " 'revi-\\\\n sion of fundamental laws and the '\n", + " 'discovery of new quantum phenomena can lead to '\n", + " 'new\\\\n technologies and algorithms for '\n", + " 'quantum computers.\\\\n Since new '\n", + " 'quantum effects are discovered seemingly every day, let’s '\n", + " 'first discuss two\\\\n basic tests that a '\n", + " 'phenomenon must pass to be able to function as a basis for '\n", + " 'quantum\\\\n computation. These are 1) The '\n", + " 'phenomenon must be nonlinear, and 2) It must be '\n", + " 'coherent.\\\\n To support quantum logic, the '\n", + " 'phenomenon must involve some form of nonlinearity, '\n", + " 'e.g.,\\\\n a nonlinear interaction between '\n", + " 'quantum degrees of freedom. Without such a '\n", + " 'nonlinearity\\\\n quantum devices, like '\n", + " 'linear classical devices, cannot perform even so simple a '\n", + " 'nonlinear\\\\n operation as an AND gate. '\n", + " 'Quantum coherence is a prerequisite for performing '\n", + " 'tasks\\\\n such as factoring using Shor’s '\n", + " 'algorithm [10], quantum simulation a la Feynman [11] '\n", + " 'and\\\\n Lloyd [12], or Grover’s data-base '\n", + " 'search algorithm [13], all of which require '\n", + " 'extended\\\\n manipulations of coherent '\n", + " 'quantum '\n", + " 'superpositions.\\\\n\\\\n '\n", + " \"1', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'066b2185-7165-468b-84f7-b8866f9baa4f', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': ' The requirements of \"\n", + " 'nonlinearity and coherence are not only necessary for a '\n", + " 'phe-\\\\nnomenon to support quantum computation, they are '\n", + " 'also in principle sufficient. As shown\\\\nin [14-15], '\n", + " 'essentially any nonlinear interaction between quantum '\n", + " 'degrees of freedom suf-\\\\nfices to construct universal '\n", + " 'quantum logic gates that can be assembled into a '\n", + " 'quantum\\\\ncomputer. In addition, the work of Preskill et '\n", + " 'al. [18] on robust quantum computation\\\\nshows that an '\n", + " 'error rate of no more than 10−4 per quantum logic '\n", + " 'operation allows one to\\\\nperform arbitrarily long quantum '\n", + " 'computations in principle.\\\\n In practice, of course, '\n", + " 'few if any quantum phenomena are likely to prove '\n", + " 'sufficiently\\\\ncontrollable to provide extended quantum '\n", + " 'computation. Promising devices under '\n", + " 'current\\\\nexperimental investigation include ion traps '\n", + " '[5,7], high finesse cavities for manipulating\\\\nlight and '\n", + " 'atoms using quantum electrodynamics [6], and molecular '\n", + " 'systems that can be\\\\nmade to compute using nuclear '\n", + " 'magnetic resonance [8-9]. Such devices store '\n", + " 'quantum\\\\ninformation on the states of quantum systems '\n", + " 'such as photons, atoms, or nuclei, and\\\\naccomplish '\n", + " 'quantum logic by manipulating the interactions between the '\n", + " 'systems via the\\\\napplication of semiclassical potentials '\n", + " 'such as microwave or laser fields. We will call '\n", + " 'such\\\\ndevices ‘conventional’ quantum computers, if only '\n", + " 'because such devices have actually '\n", + " 'been\\\\nconstructed.\\\\n There is another sense in '\n", + " 'which such computers are conventional: although the '\n", + " 'de-\\\\nvices described above have already been used to '\n", + " 'explore new regimes in physics and to\\\\ncreate and '\n", + " 'investigate the properties of new and exotic quantum '\n", + " 'states of matter, they\\\\nfunction according to well '\n", + " 'established and well understood laws of physics. Perhaps '\n", + " 'the\\\\nmost striking examples of the ‘conventionality’ of '\n", + " 'current quantum logic devices are NMR\\\\nquantum '\n", + " 'microprocessors that are operated using techniques that '\n", + " 'have been refined for\\\\nalmost half a century. Ion-trap '\n", + " 'and quantum electrodynamic quantum computers, '\n", + " 'though\\\\ncertainly cutting edge devices, operate in a '\n", + " 'quantum electrodynamic regime where the\\\\nfundamental '\n", + " 'physics has been understood for decades (that is not to '\n", + " 'say that new and\\\\nunexpected physics does not arise '\n", + " 'frequently in this regime, rather that there is '\n", + " 'general\\\\nagreement on how to model the dynamics of such '\n", + " 'devices).\\\\n Make no mistake about it: a conventional '\n", + " 'quantum logic device is the best kind of\\\\nquantum logic '\n", + " 'device to have around. It is exactly because the physics '\n", + " 'of nuclear magnetic\\\\nresonance and quantum '\n", + " 'electrodynamics are well understood that devices based on '\n", + " 'this\\\\nphysics can be used systematically to construct and '\n", + " 'manipulate the exotic quantum states\\\\nthat form the basis '\n", + " 'for quantum computation. With that recognition, let us '\n", + " 'turn '\n", + " 'to\\\\n\\\\n '\n", + " \"2', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'bcf5f805-d98f-4076-aa8c-f753cf7dbfa1', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': '‘unconventional’ quantum \"\n", + " 'computers.\\\\n Perhaps the most obvious basis for an '\n", + " 'unconventional quantum computer is the use\\\\nof particles '\n", + " 'with non-Boltzmann statistics in a refime where these '\n", + " 'statistics play a key role\\\\nin the dynamics of the '\n", + " 'device. For example, Lloyd [16] has proposed the use of '\n", + " 'fermions\\\\nas the fundamental carriers of quantum '\n", + " 'information, so that a site or state occupied by '\n", + " 'a\\\\nfermion represents a 1 and an unoccupied site or state '\n", + " 'represents a 0. It is straightforward\\\\nto design a '\n", + " 'universal quantum computer using a conditional hopping '\n", + " 'dynamics on an array\\\\nof sites, in which a fermion hops '\n", + " 'from one site to another if only if other sites are '\n", + " 'occupied.\\\\n If the array is one-dimensional, then '\n", + " 'such a fermionic quantum computer is equivalent\\\\nto a '\n", + " 'conventional quantum computer via the well-known technique '\n", + " 'of bosonization. If the\\\\narray is two or more '\n", + " 'dimensional, however, a local operation involving fermions '\n", + " 'on the\\\\nlattice cannot be mocked up by a local operation '\n", + " 'on a conventional quantum computer,\\\\nwhich must '\n", + " 'explicitly keep track of the phases induced by Fermi '\n", + " 'statistics. As a result,\\\\nsuch a fermionic computer can '\n", + " 'perform certain operations more rapidly than a '\n", + " 'conventional\\\\nquantum computer. An obvious example of a '\n", + " 'problem that can be solved more rapidly on\\\\na fermionic '\n", + " 'quantum computer is the problem of simulating a lattice '\n", + " 'fermionic system in\\\\ntwo or more dimensions. To get the '\n", + " 'antisymmetrization right in second quantized form,\\\\na '\n", + " 'conventional ‘Boltzmann’ quantum computer takes time '\n", + " 'proportional to T ℓd−1 where T\\\\nis the time over which '\n", + " 'the simulation is to take place, ℓ is the length of the '\n", + " 'lattice and\\\\nd is the dimension, while a fermionic '\n", + " 'quantum computer takes time proportional to T .\\\\n(Here we '\n", + " 'assume that the computations for both conventional and '\n", + " 'Fermionic quantum\\\\ncomputers can take advantage of the '\n", + " 'intrinsic parallelizability of such simulations: if '\n", + " 'the\\\\ncomputations are performed serially an additional '\n", + " 'factro of ℓd is required for both types\\\\nof computer to '\n", + " 'update each site sequentially.)\\\\n As the lattice '\n", + " 'size ℓ and the dimension d grow large, the difference '\n", + " 'between the two\\\\ntypes of computer also grows large. '\n", + " 'Indeed, the problem of simulating fermions hopping\\\\non a '\n", + " 'hypercube of dimension d as d → ∞ is evidently '\n", + " 'exponentially harder on a con-\\\\nventional quantum '\n", + " 'computer than a Fermionic quantum computer. Since a '\n", + " 'variety of\\\\ndifficult problems such as the '\n", + " 'travelling-salesman problem and data-base search '\n", + " 'problem\\\\ncan be mapped to particles hopping on a '\n", + " 'hypercube, it is interesting to speculate '\n", + " 'whether\\\\nfermionic computers might provide an exponential '\n", + " 'speed-up on problems of interest in ad-\\\\ndition to '\n", + " 'quantum simulation. No such problems are currently known, '\n", + " 'however. Fermionic\\\\ncomputers could be realized in '\n", + " 'principle by manipulating the ways in which electrons '\n", + " 'and\\\\n\\\\n '\n", + " \"3', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'53057fa4-ba5b-423b-a686-13c6953d5f12', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'holes hop from site to \"\n", + " 'site on a semiconductor lattice (though problems of '\n", + " 'decoherence are\\\\nlikely to be relatively severe for such '\n", + " 'systems).\\\\n It might also be possible to construct '\n", + " 'bosonic computers using photons, phonons, or\\\\natoms in a '\n", + " 'Bose-Einstein condensate. Such systems can be highly '\n", + " 'coherent and support\\\\nnonlinear interactions: phonons and '\n", + " 'photons can interact in a nonlinear fshion via '\n", + " 'their\\\\ncommon nonlinear interaction with matter, and '\n", + " 'atoms in a Bose condensate can be made\\\\nto interact bia '\n", + " 'quantum electrodynamics (by introduction of a cavity) or '\n", + " 'by collisions. So\\\\nfar, however, the feature of Bose '\n", + " 'condensates that makes them so interesting from the '\n", + " 'point\\\\nof view of physics — all particles in the same '\n", + " 'state — makes them less interesting from the\\\\npoint of '\n", + " 'view of quantum computation. Many particles in the same '\n", + " 'state, which can be\\\\nmanipulated coherently by a variety '\n", + " 'of techniques, explore the same volume of Hilbert '\n", + " 'space\\\\nas a single particle in that state. As a result, '\n", + " 'it is unclear how such a bosonic system could\\\\nprovide a '\n", + " 'speed-up over conventional quantum computation. More '\n", + " 'promising than Bose\\\\ncondensates from the perspective of '\n", + " 'quantum computation and quantum communications,\\\\nis the '\n", + " 'use of cavity quantum electrodynamics to ‘dial up’ or '\n", + " 'synthesize arbitrary states\\\\nof the cavity field. Such a '\n", + " 'use of bosonic states is important for the field of '\n", + " 'quantum\\\\ncommunications, which requires the ability to '\n", + " 'create and manipulate entangled states of\\\\nthe '\n", + " 'electromagnetic field.\\\\n A third unconventional '\n", + " 'design for a quantum computer relies on ‘exotic’ '\n", + " 'statistics\\\\nthat are neither fermionic nor bosonic. '\n", + " 'Kitaev has recently proposed a quantum '\n", + " 'computer\\\\narchitecture based on ‘anyons,’ particles that '\n", + " 'when exchanged acquuire an arbitrary phase.\\\\nExamples of '\n", + " 'anyons include two-dimensional topological defects in '\n", + " 'lattice systems of spins\\\\nwith various symmetries. Kitaev '\n", + " 'noted that such anyons could perform quantum logic '\n", + " 'via\\\\nAharonov-Bohm type interactions [19]. Preskill et '\n", + " 'al. have shown explicitly how anyonic\\\\nsystems could '\n", + " 'compute in principle [20], and Lloyd et al. have proposed '\n", + " 'methods of\\\\nrealizing anyons using superconducting '\n", + " 'circuits (they could also in principle be '\n", + " 'constructed\\\\nusing NMR quantum computers to mock up the '\n", + " 'anyonic dynamics in an effectively two-\\\\ndimensional '\n", + " 'space of spins) [21]. The advantage of using anyons for '\n", + " 'quantum computation\\\\nis that their nonlocal topological '\n", + " 'nature can make them intrinsically error-correcting '\n", + " 'and\\\\nvirtually immune to the effects of noise and '\n", + " 'interference.\\\\n As the technologies of the '\n", + " 'microscale become better developed, more and more '\n", + " 'po-\\\\ntential designs for quantum computers, both '\n", + " 'conventional and unconventional, are likely\\\\nto arise. '\n", + " 'Additional technologies that could prove useful for the '\n", + " 'construction of '\n", + " 'quantum\\\\n\\\\n '\n", + " \"4', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'de31f5a1-0290-45f3-b386-d02e3ed4c33c', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': ' logic devices include \"\n", + " 'photonic crystals, optical hole-burning techniques, '\n", + " 'electron spin res-\\\\n onance, quantum dots, '\n", + " 'superconducting circuits in the quantum regime, etc. Since '\n", + " 'every\\\\n quantum degree of freedom can in principle '\n", + " 'participate in a computation one cannot a\\\\n priori rule '\n", + " 'out the possibility of using currently hard to control '\n", + " 'degrees of freedom such as\\\\nquark and gluon in complex '\n", + " 'nuclei to process information. Needless to say, most if '\n", + " 'not all\\\\nof the designs inspired by these technologies '\n", + " 'are likely to fail. There is room for optimism\\\\nthat some '\n", + " 'such quantum computer designs will prove practicable, '\n", + " 'however.\\\\n The preceding unconventional designs for '\n", + " 'quantum computers were based on existing,\\\\nexperimentally '\n", + " 'confirmed physical phenomena (except in the case of '\n", + " 'non-abelian anyons).\\\\nLet us now turn to designs based on '\n", + " 'speculative, hypothetical, and not yet verified '\n", + " 'phenom-\\\\nena. (One of the most interesting of these '\n", + " 'phenomena is large-scale quantum computation\\\\nitself: can '\n", + " 'we create and systematically transform entangled states '\n", + " 'involving hundreds or\\\\nthousands of quantum variables?) A '\n", + " 'particularly powerful hypothesis from the point of\\\\nview '\n", + " 'of quantum computation is that of nonlinear quantum '\n", + " 'mechanics.\\\\n The conventional picture of quantum '\n", + " 'mechanics is that it is linear in the sense that '\n", + " 'the\\\\nsuperposition principle is obeyed exactly. (Of '\n", + " 'course, quantum systems can still exhibit\\\\nnonlinear '\n", + " 'interactions between degrees of freedom while continuing '\n", + " 'to obey the superpo-\\\\nsition principle.) Experiment '\n", + " 'confirms that the superposition principle is indeed '\n", + " 'obeyed\\\\nto a high degree of accuracy. Nonetheless, a '\n", + " 'number of scientists including Weinberg have\\\\nproposed '\n", + " 'nonlinear versions of quantum mechanics in which the '\n", + " 'superposition principle\\\\nis violated. Many of these '\n", + " 'proposals exhibit pathologies such as violations of the '\n", + " 'second\\\\nlaw of thermodynamics or the capacity for '\n", + " 'superluminal communication. Despite such\\\\ntheoretical '\n", + " 'difficulties, it is still possible that quantum mechanics '\n", + " 'does indeed possess a\\\\nsmall nonlinearity, even if it '\n", + " 'currently seems unlikely. If a nonlinear operation such '\n", + " 'as\\\\nthat proposed by Weinberg can be incorporated in a '\n", + " 'quantum logic operation, then the\\\\nconsequences are '\n", + " 'striking: NP-complete problems can be solved easily in '\n", + " 'polynomial time\\\\n[17]. Indeed, NP-oracle problems and all '\n", + " 'problems in #P can be solved in polynomial time\\\\non such '\n", + " 'a nonlinear quantum computer.\\\\n A general proof of '\n", + " 'this result is given in [17], however, a simple argument '\n", + " 'for why\\\\nthis is so can be seen as follows. Suppose that '\n", + " 'it is possible to perform a non-unitary\\\\noperation on a '\n", + " 'single qubit that has a positive Lyapunov exponent over '\n", + " 'some region: i.e.,\\\\nsomewhere on the unit sphere there '\n", + " 'exists a line of finite extent along which application '\n", + " 'of\\\\nthe operation causes nearby points to move apart '\n", + " 'exponentially at a rate eλ∆θ '\n", + " 'proportional\\\\n '\n", + " \"5', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'0777fa16-f0d4-4ad9-9877-a4abca0ba70f', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'to their original angular \"\n", + " 'separation δθ. Now consider a function f (x) from N bits '\n", + " 'to one\\\\nbit. We wish to determine whether or not there '\n", + " 'exists an x such that f (x) = 1, and if\\\\nso, how many '\n", + " 'such x’s there are. Using the nonlinear operation with '\n", + " 'positive Lyapunov\\\\nexponent, it is straightforward to '\n", + " 'construct a mapping leaves a point on the '\n", + " 'exponentially\\\\nexpanding line (call this point |0〉) fixed '\n", + " 'if their are no solutions to the equation f (x) = 1,\\\\nand '\n", + " 'that maps the point to a nearby point cos(n/2N )|0〉 + '\n", + " 'sin(n/2N )|1〉 along the line\\\\nif there are exactly n '\n", + " 'solutions to the equation f (x) = 1. Repeated application '\n", + " 'of the\\\\nnonlinear map can be used to drive the points '\n", + " 'apart at an exponentional rate: eventually,\\\\nat a time '\n", + " 'determined by the number of qubits N , the number of '\n", + " 'solutions n, and the rate\\\\nof spreading λ, the two points '\n", + " 'will become macroscopically distinguishable, allowing '\n", + " 'one\\\\nto determine whether or not there is a solution and '\n", + " 'if there is, how many solutions there\\\\nare. The map f '\n", + " 'need only be applied once, and the amount of time it takes '\n", + " 'to reveal the\\\\nnumber of solutions is proportional to N '\n", + " '.\\\\n The fact that nonlinear quantum mechanics allows '\n", + " 'the straightforward solution of\\\\nNP-complete and #P '\n", + " 'problems should probably be regarded as yet another strike '\n", + " 'against\\\\nnonlinear quantum mechanics. Whether or not '\n", + " 'quantum mechanics is linear is a question\\\\nto be resolved '\n", + " 'experimentally, however. In the unlikely event that '\n", + " 'quantum mechanics\\\\ndoes turn out to be nonlinear, all our '\n", + " 'problems may be solved.\\\\n Finally, let us turn our '\n", + " 'attention to hypothetical quantum Theories of '\n", + " 'Everything,\\\\nsuch as string theory. Such a theory must '\n", + " 'clearly support quantum computation since it\\\\nsupports '\n", + " 'cavity quantum electrodynamics and nuclear magnetic '\n", + " 'resonance. The obvious\\\\nquestion to ask is then, does a '\n", + " 'Theory of Everything need to support anything more '\n", + " 'than\\\\nquantum computation? So far as experimental '\n", + " 'evidence is concerned the answer to this\\\\nquestion is '\n", + " 'apparently No: we have no evident reason to doubt that the '\n", + " 'universe is at\\\\nbottom anything more than a giant, '\n", + " 'parallel, quantum information processing machine,\\\\nand '\n", + " 'that the phenomena that we observe and attempt to '\n", + " 'characterize are simply outputs\\\\nof this machine’s '\n", + " 'ongoing computation. Of course, just how the universe is '\n", + " 'carrying out\\\\nthis computation is likely to remain a '\n", + " 'question of great interest for some time.\\\\n To '\n", + " 'summarize: Computers are physical systems, and what they '\n", + " 'can do in practice and\\\\nin principle is circumscribed by '\n", + " 'the laws of physics. The laws of physics in turn permit '\n", + " 'a\\\\nwide variety of quantum computational devices '\n", + " 'including some based on nonconventional\\\\nstatistics and '\n", + " 'exotic effects. Modifications made to the laws of physics '\n", + " 'have the consequence\\\\nthat what can be computed in '\n", + " 'practice and in principle changes. A particularly '\n", + " 'intriguing\\\\n\\\\n '\n", + " \"6', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'d315d076-2b6e-4ec4-ade1-e18fb6c09998', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'variation on conventional \"\n", + " 'physics is nonlinear quantum mechanics which, if true, '\n", + " 'would\\\\nallow hard problems to be solved '\n", + " 'easily.\\\\n\\\\n '\n", + " \"7', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'7cc3c174-6e77-4928-af58-522265cace2a', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'References\\\\n\\\\n1. P. \"\n", + " 'Benioff, ‘Quantum Mechanical Models of Turing Machines '\n", + " 'that Dissipate No Energy,’\\\\nPhysical Review Letters, Vol. '\n", + " '48, No. 23, pp. 1581-1585 (1982)\\\\n2. D. Deutsch, ‘Quantum '\n", + " 'Theory, the Church-Turing Principle and the Universal '\n", + " 'Quantum\\\\nComputer,’ Proceedings of the Royal Society of '\n", + " 'London, A, Vol. 400, pp. 97-117 (1985).\\\\n3. R.P. Feynman, '\n", + " '‘Quantum Mechanical Computers,’ Optics News, Vol. 11, pp. '\n", + " '11-20\\\\n(1985); also in Foundations of Physics, Vol. 16, '\n", + " 'pp. 507-531 (1986).\\\\n4. S. Lloyd, ‘A Potentially '\n", + " 'Realizable Quantum Computer,’ Science, Vol. 261, pp. '\n", + " '1569-\\\\n1571 (1993).\\\\n5. J.I. Cirac and P. Zoller, '\n", + " '‘Quantum Computations with Cold Trapped Ions,’ '\n", + " 'Physical\\\\nReview Letters, Vol. 74, pp. 4091-4094 '\n", + " '(1995).\\\\n6. Q.A. Turchette, C.J. Hood, W. Lange, H. '\n", + " 'Mabuchi, H.J. Kimble, ‘Measurement of\\\\nConditional Phase '\n", + " 'Shifts for Quantum Logic,’ Physical Review Letters, Vol. '\n", + " '75, pp. 4710-\\\\n4713 (1995).\\\\n7. C. Monroe, D.M. Meekhof, '\n", + " 'B.E. King, W.M. Itano, D.J. Wineland, ‘Demonstration '\n", + " 'of\\\\na Fundamental Quantum Logic Gate,’ Physical Review '\n", + " 'Letters, Vol. 75, pp. 4714-4717\\\\n(1995).\\\\n8. D.G. Cory, '\n", + " 'A.F. Fahmy, T.F. Havel, ‘Nuclear Magnetic Resonance '\n", + " 'Spectroscopy: an\\\\nexperimentally accessible paradigm for '\n", + " 'quantum computing,’ in PhysComp96, Proceedings\\\\nof the '\n", + " 'Fourth Workshop on Physics and Computation, T. Toffoli, M. '\n", + " 'Biafore, J. Le˜ao, eds.,\\\\nNew England Complex Systems '\n", + " 'Institute, 1996, pp. 87-91.\\\\n9. N.A. Gershenfeld and I.L. '\n", + " 'Chuang, ‘Bulk Spin-Resonance Quantum '\n", + " 'Computation,’\\\\nScience, Vol. 275, pp. 350-356 '\n", + " '(1997).\\\\n10. P. Shor, ‘Algorithms for Quantum '\n", + " 'Computation: Discrete Log and Factoring,’ in\\\\nProceedings '\n", + " 'of the 35th Annual Symposium on Foundations of Computer '\n", + " 'Science, S. Gold-\\\\nwasser, Ed., IEEE Computer Society, '\n", + " 'Los Alamitos, CA, 1994, pp. 124-134.\\\\n11. R.P. Feynman, '\n", + " '‘Simulating Physics with Computers,’ International Journal '\n", + " 'of Theo-\\\\nretical Physics, Vol. 21, pp. 467-488 '\n", + " '(1982).\\\\n12. S. Lloyd, ‘Universal Quantum Simulators,’ '\n", + " 'Science, Vol. 273, pp. 1073-1078 (1996).\\\\n13. L.K. '\n", + " 'Grover, ‘Quantum Mechanics Helps in Searching for a Needle '\n", + " 'in a Haystack,’\\\\nPhysical Review Letters, Vol. 79, pp. '\n", + " '325-328 (1997).\\\\n14. D. Deutsch, A. Barenco, A. Ekert, '\n", + " '‘Universality in Quantum Computation,’ Proceed-\\\\nings of '\n", + " 'the Royal Society of London A, Vol. 449, pp. 669-677 '\n", + " '(1995).\\\\n\\\\n '\n", + " \"8', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'a9610b7b-a167-4f21-a917-3626fbddde3b', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Unconventional Quantum Computing Devices'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': ' 15. S. Lloyd, ‘Almost \"\n", + " 'Any Quantum Logic Gate is Universal,’ Physical Review '\n", + " 'Letters,\\\\n Vol. 75, pp. 346-349 (1995).\\\\n 16. S. Lloyd, '\n", + " '‘Fermionic Quantum Computers,’ talk delivered at the Santa '\n", + " 'Barbara work-\\\\n shop on Physics of Information, November '\n", + " '1996.\\\\n 17. D. Abrams and S. Lloyd, to be published.\\\\n '\n", + " '18. J. Preskill et al., to be published.\\\\n19. Yu. Kitaev, '\n", + " 'to be published.\\\\n20. J. Preskill et al., to be '\n", + " 'published.\\\\n21. S. Lloyd et al. to be '\n", + " 'published.\\\\n\\\\n '\n", + " \"9', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}]}, \"\n", + " \"{'documents': [{'class_name': 'Document', 'doc_id': \"\n", + " \"'278bd226-32be-4488-a970-323f579ee8e1', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'arXiv:1707.02094v1 [quant-ph] 7 Jul \"\n", + " '2017\\\\n\\\\n Quantum-dot based photonic '\n", + " 'quantum networks\\\\n '\n", + " 'Peter Lodahl\\\\n Niels '\n", + " 'Bohr Institute, University of Copenhagen, Blegdamsvej 17, '\n", + " 'DK-2100\\\\n Copenhagen, '\n", + " 'Denmark\\\\n E-mail: '\n", + " 'lodahl@nbi.ku.dk\\\\n\\\\n '\n", + " 'Abstract. Quantum dots embedded in photonic nanostructures '\n", + " 'have in recent years\\\\n '\n", + " 'proven to be a very powerful solid-state platform for '\n", + " 'quantum optics experiments. '\n", + " 'The\\\\n combination of '\n", + " 'near-unity radiative coupling of a single quantum dot to a '\n", + " 'photonic\\\\n mode and '\n", + " 'the ability to eliminate decoherence processes imply that '\n", + " 'an unprecedent\\\\n '\n", + " 'light-matter interface can be obtained. As a '\n", + " 'result, high-cooperativity '\n", + " 'photon-\\\\n emitter '\n", + " 'quantum interfaces can be constructed opening a path-way '\n", + " 'to deterministic\\\\n '\n", + " 'photonic quantum gates for quantum-information processing '\n", + " 'applications. In the\\\\n '\n", + " 'present manuscript, I review current state-of-the-art on '\n", + " 'quantum dot devices and '\n", + " 'their\\\\n applications '\n", + " 'for quantum technology. The overarching long-term goal of '\n", + " 'the research\\\\n field '\n", + " 'is to construct photonic quantum networks where remote '\n", + " 'entanglement can be\\\\n '\n", + " 'distributed over long distances by '\n", + " 'photons.\\\\n\\\\n 1. '\n", + " 'Introduction\\\\n\\\\n The ultimate vision of '\n", + " 'photonic quantum technology is to construct a complex '\n", + " 'quantum\\\\n network of stationary quantum '\n", + " 'nodes connected by flying photons in a fully '\n", + " 'quantum\\\\n way, i.e. quantum '\n", + " 'entanglement may become '\n", + " 'distributed. Such a new '\n", + " 'photonic\\\\n paradigm would have novel '\n", + " 'applications within secure quantum communication '\n", + " 'and\\\\n is proposed as a way of scaling up '\n", + " 'quantum computers. It is popularly referred to as '\n", + " 'the\\\\n ’quantum internet’ [1] and has '\n", + " 'been rooted in the atomic physics community where '\n", + " 'an\\\\n impressive proof-of-concept '\n", + " 'elementary two-node quantum network has been '\n", + " 'achieved\\\\n [2]. Solid-state alternatives '\n", + " 'to atomic single-photon emitters are attractive, since '\n", + " 'unlike\\\\n atoms they do not require '\n", + " 'complex laser cooling and trapping techniques. On '\n", + " 'the\\\\n other hand solid-state systems are '\n", + " 'often considered to be ’noisy’ in the sense that '\n", + " 'many\\\\n potential decoherence processes '\n", + " 'may deteriorate quantum properties. Remarkably, '\n", + " 'self-\\\\n assembled quantum dots (QDs) '\n", + " 'emitting single photons in the optical domain, '\n", + " 'notably\\\\n InGaAs QDs embedded in GaAs '\n", + " 'semiconductors, have matured dramatically '\n", + " 'within\\\\n the last few years. By '\n", + " 'systematically studying and combating the relevant '\n", + " 'decoherence\\\\n processes [3] impressive '\n", + " 'coherence has been demonstrated including near-perfect '\n", + " 'single-\\\\n photon indistinguishability '\n", + " '(above 98%) of two subsequently emitted photons [4, 5] '\n", + " 'and\\\\n transform-limited emission lines '\n", + " '[6]. Combined with the ability to dramatically '\n", + " 'enhance\\\\n light-matter interaction in '\n", + " 'photonic nanostructures [7], this enables '\n", + " \"near-deterministic', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'c6fdc95e-eabb-4b5e-a3c0-4c315d4c8f59', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '2\\\\n\\\\nsingle-photon sources with an internal collection '\n", + " 'efficiency exceeding 98 % [8]. This\\\\nmajor progress '\n", + " 'entails that QD single-photon sources are now gradually '\n", + " 'outperforming\\\\nthe traditional approaches based on atoms '\n", + " 'or spontaneous parametric down-conversion.\\\\nQD sources '\n", + " 'benefit from fast operation speeds, excellent stability '\n", + " 'and brightness, as well\\\\nas the potential scalability to '\n", + " 'multiple single photons and emitters. Notably, a number '\n", + " 'of\\\\nimpressive experiments have recently been implemented '\n", + " 'with QDs that have not been\\\\naccomplished on other '\n", + " 'platforms. They include the experimental demonstration of '\n", + " 'a\\\\ndeterministic entangled cluster state of strings of '\n", + " 'photons [9], boson sampling with so far\\\\nfive photons '\n", + " '[10], the detection of squeezed light correlations in '\n", + " 'resonance fluorescence\\\\n[11], and the demonstration of an '\n", + " 'on-demand entangled photon source with higher\\\\nthan 90% '\n", + " 'fidelity [12]. Time is now to build on these and other '\n", + " 'achievements in order\\\\nto scale QD photonic quantum '\n", + " 'technology and construct large and complex '\n", + " 'quantum\\\\narchitectures for quantum-information processing '\n", + " 'applications.\\\\n The present manuscript reviews the '\n", + " 'current state-of-the-art on quantum photonics\\\\nbased on '\n", + " 'QD photon emitters towards the overarching goal of '\n", + " 'constructing photonic\\\\nquantum networks. At '\n", + " 'present, a number of basic functionalities have '\n", + " 'been\\\\nsuccessfully demonstrated with generally impressive '\n", + " 'performance. These may constitute\\\\nfundamental building '\n", + " 'blocks of photonic quantum networks. In that sense a '\n", + " 'current\\\\nmajor challenge for theoretical quantum '\n", + " 'physicist is to develop resource efficient\\\\narchitectures '\n", + " 'tailored to the specific quantum hardware available, i.e., '\n", + " 'addressing how\\\\nthe basic ’puzzle pieces’ can be '\n", + " 'combined. Figure 1 illustrates the ’puzzle pieces’ '\n", + " 'already\\\\nbeing developed for the QD platform, and that '\n", + " 'will be considered here. They include\\\\nsingle-photon '\n", + " 'sources, single-photon nonlinearity, photonic circuitry, '\n", + " 'efficient-coupling\\\\nto optical fibers, on-chip '\n", + " 'single-photon detectors, and multi-emitter '\n", + " 'coupling.\\\\n QDs enable two different types of '\n", + " 'quantum resources: they may be employed\\\\nas a source of '\n", + " 'single photons or alternatively a single spin trapped in a '\n", + " 'QD may be\\\\nmanipulated as a qubit. Figure 2 illustrates '\n", + " 'these two approaches. In the former case a\\\\nsingle '\n", + " 'electron-hole pair is created in a QD by either optical '\n", + " 'excitation or by controllably\\\\ntunneling carriers into '\n", + " 'the QD with the application of an electric field. The '\n", + " 'electron-\\\\nhole pair subsequently recombines whereby a '\n", + " 'single photon is emitted by spontaneous\\\\nemission within '\n", + " 'a lifetime of typically 1 ns for a QD in a bulk medium. In '\n", + " 'the latter\\\\napproach a single carrier (electron or hole) '\n", + " 'is prepared in the QD by tunneling. The\\\\ncarrier spin is '\n", + " 'coupled to light since a photon may subsequently be '\n", + " 'absorbed by the QD\\\\ncreating a negatively (positively) '\n", + " 'charged exciton for the case of an initial electron '\n", + " '(hole).\\\\nDepending on the operational condition, the spin '\n", + " 'may have a coherence time of up to\\\\n1 μs (for holes) [13, '\n", + " '14], which means that in principle many quantum operations '\n", + " 'can\\\\nbe carried out with fast optical control techniques '\n", + " 'before decoherence sets in. For some\\\\nquantum-information '\n", + " 'applications, however, longer spin coherence times are '\n", + " 'required,\\\\ne.g., in matter based quantum-repeater '\n", + " 'architectures. To this end, the QD platform\\\\nmust be '\n", + " 'interfaced with long-lived quantum memories based on, '\n", + " 'e.g., defect centers\\\\nin diamond or atomic ensembles '\n", + " '[15]. Such hybrid quantum network architectures '\n", + " \"are\\\\noutside the scope of the present Review.', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'a19b0d3b-ebad-4d36-a615-6a4ee63a39ad', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '3\\\\n '\n", + " '~l '\n", + " '~\\\\n '\n", + " 'Nonlinearity '\n", + " 'Processing\\\\n '\n", + " 'wn\\\\n Fiber Out-coupling Single '\n", + " 'photons Detection Dipole-dipole '\n", + " 'interaction\\\\nFigure 1: Illustration of basic '\n", + " 'functionalities that can be implemented with QDs '\n", + " 'and\\\\nphotonic nanostructures. Left panel: efficient '\n", + " 'outcoupling taper sections from photonic\\\\nnanostructures '\n", + " 'to optical fibers can be engineered to obtain highly '\n", + " 'efficient outcoupling\\\\nof single photons. Center panel: a '\n", + " 'single QD in nanophotonic waveguide or cavity can '\n", + " 'be\\\\nused as a highly efficient and coherent source of '\n", + " 'single photons. Two sources are shown\\\\nin order to '\n", + " 'illustrate potential scalability of the approach. Right '\n", + " 'panel: a single QD\\\\nefficiently coupled to a waveguide '\n", + " 'may be employed as a single-photon nonlinearity\\\\n(upper '\n", + " 'left), complex photonic circuits may be constructed on a '\n", + " 'photonic chip for\\\\nquantum processing of photons (upper '\n", + " 'right), single-photon superconducting detectors\\\\nmay be '\n", + " 'implemented on-chip for highly efficient detection (lower '\n", + " 'right), and multiple\\\\nQDs may be coupled by engineering '\n", + " 'the dipole-dipole interaction in a '\n", + " 'nanophotonic\\\\nwaveguide. The figure is a courtesy of '\n", + " 'Sahand Mahmoodian.\\\\n '\n", + " '2 '\n", + " '2\\\\n Ia) '\n", + " 'hole '\n", + " 'k(b)\\\\n electron '\n", + " 'M MM\\\\nFigure 2: Photon and spin '\n", + " 'qubits generated with QDs. The light grey areas '\n", + " 'illustrate\\\\nthe regions of InAs embedded in dark grey '\n", + " 'GaAs regions thereby creating an energy\\\\nwell potential '\n", + " 'for both electrons and holes. (a) An electron-hole pair '\n", + " 'trapped in the\\\\nQD may recombine by emitting a single '\n", + " 'photon. (b) A single electron (or hole) trapped\\\\nin the '\n", + " 'QD constitutes a spin qubit (orange arrow) that may be '\n", + " \"manipulated by optical\\\\nmethods.', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'54241ad6-863a-4e89-b9c2-25ea50cede61', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '4\\\\n\\\\n2. Single-photon sources\\\\n\\\\nA QD efficiently '\n", + " 'coupled to a nanophotonic cavity or waveguide offers a '\n", + " 'promising\\\\nroute towards a deterministic single-photon '\n", + " 'source. Early pioneering work showed that\\\\nPurcell '\n", + " 'enhancement in an optical cavity may be employed to both '\n", + " 'improve brightness\\\\nand coherence of the source [16]. '\n", + " 'Ideally a useful single-photon source emits an '\n", + " 'optical\\\\npulse containing a single photon in a useful '\n", + " 'optical mode (e.g., into an optical fiber)\\\\nevery time '\n", + " 'the QD is triggered by either an optical or electrical '\n", + " 'pulse. Furthermore,\\\\nmost applications in '\n", + " 'quantum-information processing require that the emitted '\n", + " 'photons\\\\nare fully coherent, which entails that '\n", + " 'decoherence processes occurring on a time scale\\\\nof the '\n", + " 'radiative decay of the QD must be eliminated. Thus, three '\n", + " 'major figures-of-\\\\nmerit need to be optimized: i) the '\n", + " 'single-photon purity, ii) the single-photon '\n", + " 'coupling\\\\nefficiency, and iii) the indistinguishability '\n", + " 'of the emitted photons.\\\\n Re i): The discrete level '\n", + " 'structure of self-assembled QDs having widely '\n", + " 'separated\\\\noptical transitions implies that they are '\n", + " 'capable of emitting high-purity single photons\\\\n[17]. '\n", + " 'This is gauged in a Hanbury Brown - Twiss (HBT) '\n", + " 'correlation experiment by\\\\nrecording the multi-photon '\n", + " 'emission probability. By using samples with a low '\n", + " 'QD\\\\ndensity and implementing (quasi)-resonant excitation '\n", + " 'to selectively excite only a single\\\\nQD, excellent '\n", + " 'single-photon purity can be obtained at the level of '\n", + " 'g(2)(0) ≤ 0.1%, cf.\\\\nFig. 3.\\\\n Re ii): The overall '\n", + " 'coupling efficiency determines the brightness of the '\n", + " 'single-photon\\\\nsource and is comprised of several factors '\n", + " '[7]: the excitation and single-photon '\n", + " 'emission\\\\nprobability of the QD, the efficiency with '\n", + " 'which the emitted photons are channeled to a\\\\nsingle mode '\n", + " '(the β-factor), and the transfer efficiency to a low-loss '\n", + " 'propagating mode,\\\\ne.g., an optical fiber. Significant '\n", + " 'progress has been reported on all three '\n", + " 'engineering\\\\ntasks: resonant π-pulse excitation allows '\n", + " 'deterministically preparing a single exciton in\\\\na QD '\n", + " '[18] and electrically gated structures eliminate blinking '\n", + " 'between different exciton\\\\ncomplexes [19]. Embedding QDs '\n", + " 'in photonic nanostructures allows reaching a near-\\\\nunity '\n", + " 'β-factor, which has been obtained in nanophotonic '\n", + " 'waveguides [8, 20] and cavities\\\\n[4]. Finally, a variety '\n", + " 'of approaches and designs can be implemented for '\n", + " 'transferring\\\\nthe collected photons to an optical fiber '\n", + " 'using, e.g., tailored gratings for coupling light\\\\nguided '\n", + " 'on planar structures vertically off the chip, or waveguide '\n", + " 'taper sections designed\\\\nto couple directly to a fiber '\n", + " 'either by direct or evanescent coupling [21, 22, '\n", + " '23].\\\\n Re iii): The indistinguishability of an '\n", + " 'emitted photon is determined by the amount\\\\nof '\n", + " 'decoherence taking place within the emitter decay time. '\n", + " 'With a typical decay time\\\\nof nanoseconds, the primary '\n", + " 'decoherence source is coupling of the QD to phonons\\\\nand '\n", + " 'potentially photon jitter induced by relaxation processes '\n", + " 'originating from non-\\\\nresonant excitation schemes '\n", + " '[24]. The latter may be overcome by strict '\n", + " 'resonant\\\\nexcitation. Phonon interactions at the '\n", + " 'contrary are unavoidable and lead to two\\\\neffects: wide '\n", + " 'phonon sidebands and residual broadening of the '\n", + " 'zero-phonon line. The\\\\nformer contribution (containing '\n", + " 'typically about 10 % of the the total emission at\\\\nlow '\n", + " 'temperatures [7]) can readily be filtered away either by '\n", + " \"an external filter thereby', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'de71718d-112b-478e-b755-5c7d40825e52', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '5\\\\n\\\\nreducing the overall efficiency or more favorably '\n", + " 'by implementing narrow-band Purcell\\\\nenhancement. Hence '\n", + " 'the broadening of the zero-phonon line remains the '\n", + " 'fundamental\\\\ndecoherence mechanism. An in-depth '\n", + " 'theoretical study of this process including the\\\\nrole of '\n", + " 'the dimensionality of the photonic nanostructure can be '\n", + " 'found in Ref. [25].\\\\nImportantly a clear route to '\n", + " 'indistinguishable photons with near-unity visibility can '\n", + " 'be\\\\nlaid out, which has also been confirmed '\n", + " 'experimentally [4, 5, 26].\\\\n The previous discussion '\n", + " 'illustrates that QDs are capable of producing a truly '\n", + " 'on-\\\\ndemand source of single photons by implementing, in '\n", + " 'a single device, the functionalities\\\\ndemonstrated so far '\n", + " 'only in different experiments. It is likely that such a '\n", + " 'source will\\\\nbe developed experimentally soon. In '\n", + " 'particular, it is remarkable that decoherence\\\\nprocesses '\n", + " 'can be overcome in a solid-state system to such an extent '\n", + " 'that near-unity\\\\nindistinguishability between '\n", + " 'subsequently emitted photons from the same QD can '\n", + " 'be\\\\nobtained. Next step is to convert a single QD source '\n", + " 'generating photons as ”pearls\\\\non a string” into a '\n", + " 'de-multiplexed source delivering many identical single '\n", + " 'photons\\\\nsimultaneously in individual modes. Such a '\n", + " 'source can be constructed by cascading\\\\nelectro-optical '\n", + " 'switches and coupling the photons into different optical '\n", + " 'fibers in order\\\\nto overcome the time delay between '\n", + " 'subsequently emitted photons, cf. Fig. 4(a).\\\\nTo this '\n", + " 'end, the demonstration of near-transform-limited QD '\n", + " 'emission lines [6] has\\\\nthe remarkable consequence that a '\n", + " 'single QD can emit thousands of '\n", + " 'indistinguishable\\\\nphotons thereby providing a huge '\n", + " 'quantum resource. Indeed recent demonstration '\n", + " 'of\\\\nindistinguishability exceeding 90 % between two '\n", + " 'photons separated by more than 1000\\\\nemission events has '\n", + " 'proven this explicitly [27].\\\\n\\\\n3. Integrated quantum '\n", + " 'photonics\\\\n\\\\nAs detailed in the previous section, '\n", + " 'currently the most reliable approach to '\n", + " 'generate\\\\nmultiple single photons utilizes a single '\n", + " 'integrated QD source where the photons\\\\nare coupled '\n", + " 'off-chip to an optical fiber, and highly-efficient bulk '\n", + " 'optical components\\\\nimplemented for switching and '\n", + " 'routing, cf. Fig. 4(a). Nonetheless, the potential '\n", + " 'benefits\\\\nof integrating functionalities on-chip are many '\n", + " 'in terms of stability, ease of operation,\\\\nlow loss, and '\n", + " 'speed [28]. Figure 4(b) shows an architecture '\n", + " 'for an integrated de-\\\\nmultiplexed source of single '\n", + " 'photons based on a single QD in a planar '\n", + " 'nanophotonic\\\\nwaveguide platform. To implement '\n", + " 'this scheme requires the development of high-\\\\nefficiency '\n", + " 'and fast switches, and multi-port waveguide-fiber '\n", + " 'interfaces. The former\\\\nrequires the development of new '\n", + " 'devices and functionalities on-chip where so-far '\n", + " 'the\\\\nmost common approach for reconfigurable circuitry '\n", + " 'has been the application of thermo-\\\\noptic phase shifters '\n", + " '[29]. De-multiplexing requires fast switches, and two '\n", + " 'promising\\\\napproaches include electro-optical modulation '\n", + " 'implemented directly in the host material\\\\nof the QD '\n", + " '(i.e. GaAs) [30, 31] or electro-mechanical coupling '\n", + " '[32].\\\\n The crucial parameter determining the size of '\n", + " 'the photonic resource that can be\\\\ngenerated with a '\n", + " 'de-multiplexed single-photon source is the overall '\n", + " 'efficiency, including\\\\nswitching, propagation, and '\n", + " \"coupling efficiencies. Consequently the rate of N-photon', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'5982b6b2-feb7-48e0-9d25-1a0c89baaf44', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '6\\\\n '\n", + " '15 '\n", + " 'pairs '\n", + " 'taper '\n", + " 'WG\\\\n '\n", + " '~Fiber\\\\n '\n", + " 'pairs\\\\n '\n", + " 'Tum\\\\n '\n", + " 'Wch\\\\n '\n", + " 'HBT\\\\n 3 0.8 '\n", + " '= '\n", + " 'Channel\\\\n '\n", + " '1 '\n", + " 'WG\\\\n '\n", + " 'Fiber taperWG\\\\n 0.4-\\\\n '\n", + " '0.2 '\n", + " '300 '\n", + " 'r '\n", + " '140 nm\\\\n \"37.5 '\n", + " '25.0 -12.5 Delay (ns) 12.50,0 '\n", + " '25,0 '\n", + " '37.5 '\n", + " 'a\\\\n 1 0.2 Az\\\\n 3 '\n", + " '060.81.0 '\n", + " 'HOMpol:\\\\n '\n", + " '1 0.8 inputNanobeam waveguidePC 90.10 '\n", + " 'samplecryostat '\n", + " '0.8\\\\n '\n", + " '0.6 '\n", + " '0.6\\\\n '\n", + " '0.0 '\n", + " '0.4 '\n", + " '0.4 ]\\\\n ] 1.00.8\\\\n 0.6 NU '\n", + " 'pOlvIpot '\n", + " '1 0.2 Photonic-crystal '\n", + " 'waveguide 0.2\\\\n '\n", + " '0.4\\\\n '\n", + " '0.2 '\n", + " '1 '\n", + " '0.60.4\\\\n '\n", + " '0.2 '\n", + " '0.4\\\\n '\n", + " '0.6\\\\n '\n", + " '0.2\\\\n '\n", + " 'Time delay '\n", + " '(ns) '\n", + " '850 900 '\n", + " '950 '\n", + " '1000\\\\n '\n", + " 'Wavelength (nm)\\\\nFigure '\n", + " '3: Purity, '\n", + " 'indistinguishability, and out-coupling efficiency of QD '\n", + " 'single-\\\\nphoton '\n", + " 'sources. '\n", + " 'Left column: '\n", + " 'Examples of HBT and Hong-Ou-Mandel '\n", + " '(HOM)\\\\nindistinguishability measurements by implementing '\n", + " 'resonant excitation on a QD in\\\\na micropillar cavity '\n", + " '(upper figure shows the cavity structure). g(2)(0) = 0.009 '\n", + " 'and\\\\na single-photon indistinguishability of 96.4% is '\n", + " 'extracted from the two sets of data.\\\\nFigures reproduced '\n", + " 'from Ref. [5]. Right column: Examples of devices made for '\n", + " 'out-\\\\ncoupling single photon from a QD in a planar '\n", + " 'nanophotonic waveguide to an optical\\\\nfiber with high '\n", + " 'efficiency by tailored evanescent coupling. The upper '\n", + " 'panel is reproduced\\\\nfrom Ref. [21]. Lower panel: '\n", + " 'application of this method to couple out single '\n", + " 'photons\\\\nfrom high β-factor nanophotonic waveguides, '\n", + " 'i.e., nanobeam waveguides and photonic-\\\\ncrystal '\n", + " 'waveguides. By recording the reflection and transmission '\n", + " 'spectrum of the device,\\\\na chip-to-fiber coupling '\n", + " 'efficiency of > 80% is obtained. Data reproduced from Ref. '\n", + " '[23].\\\\n\\\\ngeneration is RN = Rpump ×ηN /N , where η is '\n", + " 'the overall transmission efficiency from the\\\\nsource to '\n", + " 'the fiber and Rpump is the repetition rate of the '\n", + " \"excitation laser. Exemplarily', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'22f52865-e757-49a9-8244-cad99bf66572', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '7\\\\n '\n", + " 'PC (b) '\n", + " 'Electrically-controlled\\\\n '\n", + " 'switches\\\\n '\n", + " 'de-muitipieett '\n", + " 'QD 8-98% Array of delay '\n", + " 'fibers\\\\n '\n", + " '2\\\\n '\n", + " 'Oei\\\\n '\n", + " 'Photonic crystal\\\\n ultte ~fow-Ioss deleco '\n", + " 'e waveguide\\\\n photonic '\n", + " 'circuit\\\\nFigure 4: Illustrations of schemes for '\n", + " 'de-multiplexing a single-photon source. (a)\\\\na '\n", + " 'five-photon source obtained by cascading four Pockels '\n", + " 'cells (PCs) and polarizing\\\\nbeamsplitters to couple '\n", + " 'subsequently emitted photons from the QD into different '\n", + " 'optical\\\\nfibers with different delay lengths. This '\n", + " 'de-multiplexed source is applied for proof-\\\\nof-concept '\n", + " 'boson sampling. Figure reproduced from '\n", + " 'Ref. [10]. (b) Architecture\\\\nof an '\n", + " 'integrated de-multiplexed source based on a QD coupled '\n", + " 'with high efficiency\\\\nto a photonic-crystal waveguide. '\n", + " 'The emitted photons are transferred to '\n", + " 'dielectric\\\\nwaveguides and subsequently routed by '\n", + " 'electrical switches and coupled off-chip to\\\\ndifferent '\n", + " 'fiber delays in order to compensate for the time delay '\n", + " 'in-between photons.\\\\nThe figure is a courtesy of Leonardo '\n", + " 'Midolo.\\\\n\\\\nconsidering η = 50% and Rpump = 80 MHz '\n", + " '(corresponding to the repetition rate of a\\\\nTi:Sapph. '\n", + " 'laser) corresponds to a 10-photon generation rate of R10 ∼ '\n", + " '8 kHz, which\\\\nillustrates the promising prospects of this '\n", + " 'approach while posing clear benchmarks for\\\\nthe '\n", + " 'efficiency of the applied switching and coupling '\n", + " 'technology.\\\\n Integrating a de-multiplexed source on '\n", + " 'a chip is one immediate goal. Another\\\\nimportant task is '\n", + " 'to develop complex integrated tunable photonic circuits '\n", + " 'that can\\\\nprocess the generated single photons leading to '\n", + " 'applications for quantum simulations\\\\n[33]. Current '\n", + " 'state-of-the-art of this technology is a six-mode '\n", + " 'reprogrammable circuit\\\\ncapable of implementing universal '\n", + " 'high-fidelity quantum gates [29]. Considerable effort\\\\nis '\n", + " 'directed to the scaling of these circuits in working '\n", + " 'towards a fully integrated system\\\\nwhere photon source '\n", + " 'and processing circuit would be implemented on a single '\n", + " 'photonic\\\\nchip. However, implementing on-chip the '\n", + " 'relatively long optical delays (typically tens '\n", + " 'of\\\\nnanoseconds) required to interfere subsequently '\n", + " 'emitted photons, poses a challenge for\\\\nthis approach in '\n", + " 'requiring slow-light optical buffers. Consequently at '\n", + " 'present it seems\\\\nmost realistic to consider approaches '\n", + " 'based on two separate chips, connected by '\n", + " 'optical\\\\nfibers, where one chip constitutes the source '\n", + " 'and the other chip the quantum processor.\\\\n The '\n", + " 'operation wavelength of a quantum photonics processor is '\n", + " 'an essential\\\\nparameter. The preferred operation '\n", + " 'wavelength is within the telecom C-band (1.55 μm),\\\\nwhere '\n", + " 'advanced high-performance optical components and fiber '\n", + " 'technology developed\\\\nfor telecom industry are present. '\n", + " 'The natural emission wavelength of self-assembled '\n", + " 'high\\\\nquality QDs is around 950 nm and while significant '\n", + " 'efforts are taken to develop QDs in\\\\nthe telecom bands as '\n", + " 'well [34, 35] the excellent performance in terms of '\n", + " 'coherence and\\\\nefficiency required for quantum technology '\n", + " \"has not yet been achieved. An alternative', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'6005404e-7547-45e3-b282-52c7be36abcf', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '8\\\\n\\\\napproach employs frequency down-conversion of '\n", + " 'single photons from the near-infrared\\\\nto the telecom '\n", + " 'band. Noise free conversion of single photons from a QD '\n", + " 'with an\\\\nefficiency exceeding 30% has been demonstrated '\n", + " '[36], which could be further increased by\\\\nimproving the '\n", + " 'in- and out-coupling efficiency through the nonlinear '\n", + " 'conversion crystal.\\\\nThe two-chip approach alluded to '\n", + " 'above has the asset that the frequency conversion\\\\nstep '\n", + " 'could naturally be incorporated in between the source chip '\n", + " 'and the processing\\\\nchip. Furthermore, implementing '\n", + " 'frequency conversion of QD photons also has '\n", + " 'another\\\\nadvantage, since it provides a way of overcoming '\n", + " 'spectral inhomogeneities of photons\\\\nemitted by different '\n", + " 'QDs by transducing them to the same wavelength in the '\n", + " 'C-band\\\\nwith the implementation of a tunable pump laser. '\n", + " 'This could prove a powerful way of\\\\nscaling up QD systems '\n", + " 'to couple multiple emitter qubits (e.g., spins) in '\n", + " 'addition to the\\\\nmultiple photon qubits already '\n", + " 'considered.\\\\n Finally, another essential requirement '\n", + " 'is the ability to detect optical photons with\\\\nvery high '\n", + " 'efficiency. Very significant progress has been obtained in '\n", + " 'recent years with the\\\\ndevelopment of superconducting '\n", + " 'nanowire single-photon detectors [37], where the '\n", + " 'single-\\\\nphoton detection efficiency today approaches '\n", + " '100% and the speed is compatible with QD\\\\nsources. '\n", + " 'Importantly these detectors can naturally be integrated on '\n", + " 'the planar GaAs\\\\nplatform containing QDs [38], which '\n", + " 'means that photon source, circuit, and detection\\\\ncould '\n", + " 'potentially be integrated on a single chip, which may pave '\n", + " 'the way to ultimately\\\\nlow-loss photonic quantum '\n", + " 'nodes.\\\\n\\\\n4. Single-photon nonlinearity\\\\n\\\\nThe '\n", + " 'previous sections concerned the generation of single '\n", + " 'photons and their subsequent\\\\nquantum interference in '\n", + " 'photonic circuits. Many quantum-information '\n", + " 'applications\\\\nof photons require generating photon-photon '\n", + " 'interactions. Generally photons interact\\\\nweakly meaning '\n", + " 'that standard nonlinearities are typically too weak to be '\n", + " 'operational\\\\nat the level of single photons. However, the '\n", + " 'efficient coupling of single QDs to '\n", + " 'photonic\\\\nnanostructures imply that such a system may be '\n", + " 'exploited to mediate a giant nonlinear\\\\nresponse '\n", + " 'operational at the level of single photons. Two different '\n", + " 'approaches can be\\\\ntaken to reach a giant nonlinearity '\n", + " 'with a QD or any other quantum emitter: the QD\\\\ncan be '\n", + " 'strongly coupled to a cavity and the nonlinearity of the '\n", + " 'Jaynes-Cummings ladder\\\\nexploited [39, 40], or a QD very '\n", + " 'efficiently coupled to a single optical mode in a '\n", + " 'cavity\\\\n[41, 42] or waveguide [43] may be used as a '\n", + " 'saturable nonlinearity. The latter approach\\\\nis '\n", + " 'beneficial for ease of operation, since it is less '\n", + " 'sensitive to the exact tuning of the\\\\nwavelength of the '\n", + " 'QD. Furthermore, the saturable QD nonlinearity generally '\n", + " 'can be\\\\noperated at a weaker incident photon flux [43]. '\n", + " 'The key operational principle of the QD\\\\nnonlinearity is '\n", + " 'as follows: a narrow-band pulse containing a single photon '\n", + " 'is reflected\\\\nfrom a QD in a waveguide due to destructive '\n", + " 'interference of forward scattering and the\\\\nincoming '\n", + " 'field. The efficient coupling (high β-factor) and coherent '\n", + " 'interaction, which\\\\ncan be achieved with QDs, implies '\n", + " 'that the interaction between the photon and the\\\\nQD is '\n", + " 'deterministic. Since a two-level emitter can only scatter '\n", + " \"one photon at a time,', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'c1b95287-4dfa-411d-8269-8450b9dac1db', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '9\\\\n\\\\ntwo photons have an increased probability to be '\n", + " 'transmitted past the QD leading to a\\\\nphoton sorting '\n", + " 'process. From an experimental point of view this type of '\n", + " 'nonlinearity\\\\nis attractive since no active control over '\n", + " 'the quantum state of the QD is required,\\\\ni.e., the QD is '\n", + " 'merely exploited as a passive scatterer. On the other '\n", + " 'hand, the photon\\\\nsorting is not ideal even for a perfect '\n", + " 'coupling efficiency. This implies that while a '\n", + " 'single-\\\\nphoton component of a pulse may be '\n", + " 'deterministically reflected, two- and '\n", + " 'higher-photon\\\\ncomponents are only partially transmitted '\n", + " 'and in this process entanglement is generated\\\\nbetween '\n", + " 'the two photons [44]. One approach of overcoming this '\n", + " 'limitation and obtain\\\\ndeterministic photon sorting has '\n", + " 'been suggested by combining the QD nonlinearity with\\\\na '\n", + " 'nonlinear spatio-temporal mode selector [45]. Such an '\n", + " 'approach may be applied for\\\\nconstructing a '\n", + " 'resource-efficient Bell-state analyzer [44, 45], cf. '\n", + " 'Section 7. It should be\\\\nmentioned that having a '\n", + " 'three-level emitter with coherent control of the level '\n", + " 'populations\\\\nin the waveguide opens a range of additional '\n", + " 'opportunities. Such level schemes can be\\\\nimplemented '\n", + " 'with charged QDs, cf. the description in the next section. '\n", + " 'One exciting\\\\nproposal is that of a photonic transistor '\n", + " 'controlled by only a single photon [46].\\\\n\\\\n5. '\n", + " 'Spin-photon interfaces\\\\n\\\\nIntroducing a single spin in a '\n", + " 'QD leads to a range of additional opportunities. It '\n", + " 'grants\\\\naccess to two separate ground states (spin up or '\n", + " 'down) that can be exploited as a\\\\nquantum memory to '\n", + " 'encode a qubit. Either single holes or electrons can be '\n", + " 'employed\\\\nand may be deterministically prepared by '\n", + " 'controlling the tunnel barriers in '\n", + " 'electrically\\\\ncontacted semiconductor structures. Various '\n", + " 'level structures can be implemented by the\\\\nuse of Zeeman '\n", + " 'tuning with a magnetic field, and spin initialization, '\n", + " 'manipulation, and\\\\nread-out can be achieved rapidly with '\n", + " 'short optical pulses (typically in the '\n", + " 'nanosecond\\\\nrange), cf. Ref. [19] for a detailed '\n", + " 'review of the physics of single spins in QDs.\\\\nThe spin '\n", + " 'lifetime can be in the range of milliseconds or longer in '\n", + " 'a large magnetic\\\\nfield, which is determined by '\n", + " 'phonon-mediated spin relaxation processes. However,\\\\nin '\n", + " 'quantum applications the coherence time of the spin is the '\n", + " 'essential parameter.\\\\nA coherence time of up to T 2∗ > '\n", + " '460 ns has been reported for hole spins that has\\\\nthe '\n", + " 'advantage compared to electrons that they do not suffer '\n", + " 'from the Fermi contact\\\\nhyperfine interaction with '\n", + " 'nuclear spins [14]. Importantly the spin coherence time '\n", + " 'can\\\\nbe several orders of magnitude longer than the spin '\n", + " 'manipulation time, which means\\\\nthat multiple quantum '\n", + " 'gate operations could be implemented within the spin '\n", + " 'coherence\\\\ntime. Unravelling spin decoherence mechanisms '\n", + " 'in QDs is a research topic in itself and is\\\\noutside the '\n", + " 'scope of the present review. For a thorough discussion, '\n", + " 'see Refs. [19, 47]. It\\\\nis anticipated that further '\n", + " 'research efforts will enable increasing the spin coherence '\n", + " 'time\\\\nfurther. Nonetheless, it seems unlikely that it '\n", + " 'will be possible to extend the coherence\\\\ntime by many '\n", + " 'orders of magnitude in present QD systems. In particular, '\n", + " 'long-range\\\\nquantum repeater protocols would require '\n", + " 'millisecond range quantum memories. This\\\\nentails a '\n", + " 'hybrid approach where the single photons generated by QDs '\n", + " 'are interfaced\\\\nwith long-lived quantum memories, e.g., '\n", + " \"vacancy centers in diamond [48], ions [49], or', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'093afd39-07a0-4589-a0c9-eb679ce77c2f', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': ' Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '10\\\\n\\\\n potentially mechanical systems.\\\\n '\n", + " 'Considering a charged exciton in an external magnetic '\n", + " 'field, various four-level\\\\n emitter schemes can be '\n", + " 'engineered with two ground states corresponding to '\n", + " 'either\\\\n (pseudo)spin up or down of the electron (hole). '\n", + " 'The recombination of a charged exciton\\\\n leads to the '\n", + " 'emission of polarized photons, where the polarization of '\n", + " 'the photon is\\\\n correlated with the resulting spin state. '\n", + " 'In this way entanglement between the QD\\\\n spin and the '\n", + " 'polarization of the emitted photon can be generated [13, '\n", + " '50] where\\\\n in general hyper-entanglement between spin, '\n", + " 'polarization, and frequency is obtained.\\\\n Subsequently '\n", + " 'the information in one of the variables can be erased to '\n", + " 'create a Bell state.\\\\n This has been the resource '\n", + " 'enabling quantum teleportation between a single photon '\n", + " 'and\\\\n a solid-state spin [51]. A further extension of '\n", + " 'this experiment was the demonstration of\\\\n heralded '\n", + " 'entanglement between two spatially separate hole spins in '\n", + " 'QDs [52], cf. Fig. 5\\\\n for the experimental '\n", + " 'layout.\\\\n Another possibility is to couple QD spins '\n", + " 'directly to photon path by exploiting\\\\n the concept of '\n", + " 'chiral light-matter interaction [53]. By tailoring the '\n", + " 'local electric field\\\\n in a nanophotonic waveguide to '\n", + " 'match the in-plane circular dipole moment of one of\\\\n the '\n", + " 'charged exciton transitions, it is possible to induce '\n", + " 'deterministic directional photon\\\\n emission [54], i.e. σ± '\n", + " 'transitions emit in different directions. This effect was '\n", + " 'used\\\\nto demonstrate an interface between QD spin and the '\n", + " 'photon path [55]. Based on\\\\nthis functionality a photonic '\n", + " 'quantum network architecture was put forward and '\n", + " 'its\\\\nfeasibility evaluated for QDs in nanophotonic '\n", + " 'waveguides [56], cf. Fig. 5.\\\\n\\\\n6. Coupling of multiple '\n", + " 'quantum dots\\\\n\\\\nWe have seen in previous sections how a '\n", + " 'single QD deterministically coupled to a single\\\\noptical '\n", + " 'mode may be exploited as a source of multiple photonic '\n", + " 'qubits potentially\\\\ngenerating a large quantum resource. '\n", + " 'It is natural to consider the option of scaling\\\\nfurther '\n", + " 'up by coupling multiple QDs. This is generally a demanding '\n", + " 'task, as it is\\\\nthe case for any solid-state optical '\n", + " 'emitter, since inhomogeneous broadening of a QD\\\\nensemble '\n", + " '(typically at the level of THz, which should be compared '\n", + " 'to the sub-GHz\\\\nhomogenous linewidth of a QD) implies '\n", + " 'that significant spectral mismatch would need\\\\nto be '\n", + " 'overcome. Various tuning methods of QDs have been '\n", + " 'implemented including\\\\nelectric-field tuning [57] and '\n", + " 'strain tuning [58] that needs to be implemented locally '\n", + " 'to\\\\neach individual QD. Eventually not only the central '\n", + " 'emission frequency of the different\\\\nemitters must be '\n", + " 'controlled, but the complete photon pulses need to be '\n", + " 'matched in\\\\norder to faithfully couple multiple QDs. An '\n", + " 'important figure-of-merit of the success is\\\\ngauged in a '\n", + " 'quantum-interference experiment between two separate QD '\n", + " 'single-photon\\\\nsources, where an interference visibility '\n", + " 'of 93% has been reported in a weak-excitation\\\\nregime '\n", + " '[59], which is a highly encouragin result for this '\n", + " 'approach.\\\\n Given the ability to radiatively couple '\n", + " 'two or more QDs, a photonic nanostructure\\\\ncan be '\n", + " 'constructed to enhance and tailor the coupling. Multiple '\n", + " 'QDs coupled by\\\\ndipole-dipole interaction lead to the '\n", + " \"formation of collective quantum states that can', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'39ce6fe7-fe46-40b9-9d03-35a978fc7363', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '11\\\\n Entanglement '\n", + " 'and\\\\n non-local measurement\\\\n '\n", + " 'Spin preparation and Spin preparation and '\n", + " 'local (a) Chiral element\\\\n local '\n", + " 'measurements (QDI) Ti Sapphiremeasurements '\n", + " '(QD2)\\\\n '\n", + " 'laser\\\\n '\n", + " 'Diode '\n", + " 'las '\n", + " '0 +\\\\n Diode '\n", + " 'lasers '\n", + " '/1)\\\\n '\n", + " 'EOM (b) '\n", + " 'Network node\\\\n '\n", + " 'EOMs '\n", + " 'EOMs\\\\n '\n", + " 'Intranode parity measurement Internode '\n", + " 'entanglement\\\\n '\n", + " 'BSI '\n", + " 'Detector 0 '\n", + " 'Detector\\\\n '\n", + " 'Emitter '\n", + " '0\\\\n '\n", + " 'Fiber '\n", + " 'Emitter\\\\n '\n", + " 'Single photon Communication '\n", + " 'photon\\\\n '\n", + " 'source\\\\n N Gratings '\n", + " 'BS2\\\\n '\n", + " '(c) Linked Nodes\\\\n '\n", + " 'filters\\\\n Single-photon '\n", + " 'detectors '\n", + " 'Photon routing\\\\n '\n", + " 'QDI '\n", + " 'OD2\\\\n 5 m\\\\nFigure '\n", + " '5: Left panel: experimental setup employed for '\n", + " 'the generation of remote\\\\nentanglement between two QD '\n", + " 'hole spins in two different cryostats separated by\\\\n5 m. '\n", + " 'Figure reproduced from '\n", + " 'Ref. [52]. Right '\n", + " 'panel: Architecture of a photonic\\\\nquantum network based '\n", + " 'on chiral photon-emitter elements (a) embedded in '\n", + " 'on-chip\\\\ninterferometers constituting fundamental quantum '\n", + " 'nodes (b). The quantum nodes are\\\\nlinked together by '\n", + " 'photons for remote entanglement distribution (c). Figure '\n", + " 'reproduced\\\\nfrom Ref. [56].\\\\n\\\\nhave sub- or '\n", + " 'super-radiant character. In photonic nanostructures, the '\n", + " 'dipole-dipole\\\\ninteraction can be significantly extended '\n", + " 'beyond the behavior found in a homogenous\\\\n(3D) medium, '\n", + " 'where the coupling falls off rapidly with distance d '\n", + " '(scales as d−3). In\\\\na 1D waveguide with radiative '\n", + " 'coupling to the waveguide as the dominating '\n", + " 'process,\\\\ndipole-dipole interaction may be infinitely '\n", + " 'extended, i.e. in practice it is limited by\\\\nthe '\n", + " 'absorption or scattering loss of the guided mode. '\n", + " 'Controlling the distance between\\\\nmultiple emitters '\n", + " 'enables the switching between sub- and super-radiant '\n", + " 'behavior. Apart\\\\nfrom the fundamental interest, the '\n", + " 'long-range dipole-dipole coupling may be exploited\\\\nto '\n", + " 'construct quantum gates between stationary qubits [60]. '\n", + " 'Introducing chiral coupling\\\\nopens new opportunities by '\n", + " 'the ability to engineer the directional coupling '\n", + " 'between\\\\nemitters. This may lead to novel opportunities '\n", + " 'for quantum simulators and for entering\\\\nnew regimes of '\n", + " 'coupled photons and emitters. For instance directional '\n", + " 'coupling has been\\\\nfound to lead to the formation of '\n", + " 'quantum dimers in the steady state of the system '\n", + " '[61].\\\\nAnother approach to scaling to multiple emitters '\n", + " 'exploits tunnel coupling between QDs\\\\ngrown in close '\n", + " 'mutual vicinity. With such an approach advanced level '\n", + " 'structures can\\\\nbe engineered in these QD molecules where '\n", + " 'it has been demonstrated that the spin\\\\ncoherence time '\n", + " \"can be significantly extended [62].', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'5516db24-13d0-408f-a09f-df9415a927b9', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '12\\\\n\\\\n7. Quantum network architectures\\\\n\\\\nWe have '\n", + " 'reviewed various basic quantum functionalities and '\n", + " 'hardware that can be\\\\nimplemented with the use of QDs '\n", + " 'embedded in modern nanophotonic structures. It\\\\nis a '\n", + " 'pertinent task, to consider what applications and actual '\n", + " 'architectures can be\\\\nimplemented with these quantum '\n", + " 'resources and how to make them most resource\\\\nefficient. '\n", + " 'This section briefly highlights some of the opportunities '\n", + " 'and future directions.\\\\nA general observation, as '\n", + " 'discussed in detail above, is that QD systems can '\n", + " 'generate\\\\nmany single-photon qubits, while the scaling up '\n", + " 'to many matter qubits is expected to\\\\nbe much more '\n", + " 'challenging. It is important to have these general '\n", + " 'guidelines in mind\\\\nwhen designing resource efficient '\n", + " 'quantum network architectures that is tailored to '\n", + " 'the\\\\nquantum hardware based on QDs.\\\\n As discussed '\n", + " 'in the present Review, QDs in nanophotonic structures are '\n", + " 'very well\\\\nsuited for generating highly coherent single '\n", + " 'photons on demand at a high repetition\\\\nrate. Such a '\n", + " 'source can generate thousands of indistinguishable photons '\n", + " '[27] that if\\\\nefficiently routed could be an important '\n", + " 'photonic resource for quantum simulations '\n", + " '[33].\\\\nFurthermore, recent developments on how to '\n", + " 'efficiently fuse photonic qubits with linear\\\\noptics have '\n", + " 'the renewed excitement on how to construct single-photon '\n", + " 'based quantum\\\\ncomputing [63].\\\\n Introducing a '\n", + " 'single-photon nonlinearity to the toolbox opens for a '\n", + " 'range of\\\\nadditional possibilities. The most simple case '\n", + " 'is that of a two-level emitter, and here\\\\ndistortion of '\n", + " 'the optical pulses associated with photon-emitter bound '\n", + " 'state contributions\\\\nhas been interpreted as essentially '\n", + " 'a ”no-go theorem” for quantum computation [64].\\\\nOne work '\n", + " 'around, however, has been identified where the pulse '\n", + " 'distortion is actually\\\\nexploited such that one- and '\n", + " 'two-photon pulses are scattered into orthogonal '\n", + " 'spectral-\\\\ntemporal modes that can subsequently be '\n", + " 'separated, e.g., by nonlinear frequency\\\\nconversion [45]. '\n", + " 'With such a functionality all path-encoded photonic Bell '\n", + " 'states can\\\\nbe deterministically measured exploiting only '\n", + " 'four non-linear interaction process, linear\\\\noptics, and '\n", + " 'photon detection, cf. Fig. 6. Based on a Bell-state '\n", + " 'analyzer, quantum\\\\ncomputing can in principle be '\n", + " 'implemented by teleportation based gates and '\n", + " 'linear\\\\noptical quantum computing [65]. Alternatively, a '\n", + " 'photonic CZ gate can be directly\\\\nbased on the two-level '\n", + " 'emitter nonlinearity. However, in this case the pulse '\n", + " 'distortion\\\\nintroduced by photon scattering has to be '\n", + " 'undone by scattering an additional time on\\\\nthe emitter. '\n", + " 'However, since this scattering process is not time '\n", + " 'symmetric the photon\\\\npulse would need to be reverted in '\n", + " 'time in between the two scattering process. Such a\\\\ntime '\n", + " 'reversal of the photon pulses could in principle be '\n", + " 'obtained by storing, reverting,\\\\nand releasing photon '\n", + " 'pulses in a gradient-echo quantum memory.\\\\n Having '\n", + " 'access to a 3-level Λ-scheme with two stable ground states '\n", + " 'leads to many\\\\nmore opportunities since now the emitter '\n", + " 'can store a qubit for extended times. Coherent\\\\ncontrol '\n", + " 'of the qubit state enables implementing a Duan-Kimble CNOT '\n", + " 'gate for photons\\\\nwhere two subsequently interacting '\n", + " 'photons are entangled by their successive '\n", + " 'interaction\\\\nwith the same emitter [66]. Another related '\n", + " \"approach employs single-photon Raman', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'d1065b19-dd0a-4177-9589-f30e9ba2ae04', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '13\\\\n 1 photon\\\\n '\n", + " 'TLS SFG '\n", + " 'components\\\\n '\n", + " '2 '\n", + " 'photon\\\\n '\n", + " 'components\\\\n classical '\n", + " 'dichroic\\\\n pump '\n", + " 'splitter\\\\n Q1\\\\n Q2\\\\nFigure 6: Illustration of '\n", + " 'a deterministic Bell-state analyzer based on '\n", + " 'single-photon\\\\nnonlinearity, frequency conversion, and '\n", + " 'linear optics. Upper plot: a deterministic two-\\\\nlevel '\n", + " 'scattering (TLS) process induced, e.g., in a nanophotonic '\n", + " 'waveguide may be\\\\noperated such that one and two photons '\n", + " 'are scattered to orthogonal spatio-temporal\\\\nmodes. The '\n", + " 'two modes can subsequently be selected by implementing '\n", + " 'active filtering,\\\\ne.g. nonlinear sum-frequency '\n", + " 'generation (SFG) where the spatio-temporal mode of '\n", + " 'the\\\\nclassical pump is matched to, e.g., the one-photon '\n", + " 'component of the scattered light.\\\\nConsequently, only the '\n", + " 'single-photon component is frequency converted and can '\n", + " 'be\\\\nseparated from the two-photon component by a dichroic '\n", + " 'splitter. Lower plot: optical\\\\ncircuit containing four '\n", + " 'photon-sorting elements that is capable of determining all '\n", + " 'four\\\\npath-encoded Bell states by photon coincidence '\n", + " 'detection. Figure reproduced from '\n", + " 'Ref.\\\\n[45].\\\\n\\\\ninteraction to deterministically swap '\n", + " 'the quantum state of the emitter and a photon\\\\n[67, 68]. '\n", + " 'A full architecture of a quantum network based on QDs '\n", + " 'chirally coupled to\\\\nnanophotonic waveguides has been put '\n", + " 'forward in Ref. [56], cf. Fig. 5. Here the very\\\\nlarge '\n", + " 'cooperativity obtainable with QDs leads to a gate fidelity '\n", + " 'approaching unity for\\\\nexperimentally realistic '\n", + " 'parameters.\\\\n Another highly exciting direction is '\n", + " 'to exploit a single QD efficiently coupled to '\n", + " 'a\\\\nnanophotonic waveguide to directly produce entangled '\n", + " \"photons. To this end, Lindner', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'a5cf3e02-ee6b-47ea-9b4d-e330002f40f5', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '14\\\\n\\\\nand Rudolph have put forward the proposal of a '\n", + " 'photonic ”cluster state machine gun”\\\\nbased on a QD that '\n", + " 'could potentially producing long strings of photons in an '\n", + " 'entangled\\\\ncluster state [69]. In this proposal, a single '\n", + " 'QD spin is brought into a superposition\\\\nstate of up and '\n", + " 'down and optically excited to a superposition of charged '\n", + " 'exciton states.\\\\nSubsequently the QD decays by '\n", + " 'spontaneous emission of a single photon thereby '\n", + " 'creating\\\\nan entangled state between the polarization of '\n", + " 'the emitted photon and the spin state.\\\\nThe '\n", + " 'excitation-emission process can be repeated multiple times '\n", + " 'in which case a large\\\\nGHZ photonic state is generated. '\n", + " 'If π/2 rotations on the QD spin are carried '\n", + " 'out\\\\nin-between photon emissions, an entangled photonic '\n", + " 'cluster state is created. A QD\\\\nembedded in a '\n", + " 'nanophotonic waveguide could potentially serve as a source '\n", + " 'of large 1D\\\\ncluster states, where the cluster size is '\n", + " 'determined by the number of emission events\\\\n(determined '\n", + " 'by the QD lifetime that is typically ∼ 100 ps in a '\n", + " 'nanophotonic structure)\\\\nthat can take place within the '\n", + " 'coherence time of the spin (up to ∼ μs). A '\n", + " 'pioneering\\\\nexperiment of photonic cluster state '\n", + " 'generation with a QD source has recently been\\\\nreported '\n", + " '[9], which validates the approach.\\\\n Cluster states '\n", + " 'have attracted a lot of attention since it was realized '\n", + " 'that they enable\\\\nuniversal quantum computing. The '\n", + " 'general philosophy behind such one-way quantum\\\\ncomputing '\n", + " 'architectures [70] is that a large scale entangled state '\n", + " 'is generated ”up front”\\\\nand computation carried out by '\n", + " 'single-qubit operations induced by '\n", + " 'measurements.\\\\nImportantly, quantum computing requires '\n", + " 'access to 2D photonic cluster states as\\\\nopposed to the '\n", + " '1D cluster states considered above. '\n", + " 'Several proposals of how to\\\\nachieve this with QDs have '\n", + " 'been put forward. One approach is to optically '\n", + " 'couple\\\\ntwo QDs [71], which requires local tuning of the '\n", + " 'two QDs into mutual resonance. An\\\\nalternative proposal '\n", + " 'has been put forward based on a single quantum emitter, '\n", + " 'where the\\\\nemitted photons are controllably coupled back '\n", + " 'to interact with the emitter and through\\\\nthis process '\n", + " 'generates the 2D cluster [72]. Such a ”one-emitter '\n", + " 'quantum-computer\\\\narchitecture” is almost ideally suited '\n", + " 'for implementation based on QDs in '\n", + " 'nanophotonic\\\\nstructures. Another exciting potential '\n", + " 'applications of photonic cluster states would be '\n", + " 'in\\\\nquantum repeater protocols without the necessity of a '\n", + " 'matter-based quantum memory\\\\n[73, 74]. Such an approach '\n", + " 'would potentially overcome the main bottleneck of the '\n", + " 'QD\\\\nbased platform for quantum networks resulting from '\n", + " 'the relatively short-lived quantum\\\\nmemory offered by the '\n", + " 'QD spin.\\\\n\\\\n8. Conclusions\\\\n\\\\nQDs in photonic '\n", + " 'nanostructures constitute an unprecedent photon-emitter '\n", + " 'interface at\\\\noptical frequencies. The recent significant '\n", + " 'progress implies that advanced quantum-\\\\ninformation '\n", + " 'processing protocols now become within experimental reach '\n", + " 'in the overall\\\\nquest towards constructing photonic '\n", + " 'quantum networks. Such a distributed photonic\\\\nquantum '\n", + " 'network would open new avenues for secure quantum '\n", + " 'communication and\\\\nultimately provide a way of scaling up '\n", + " 'quantum computers. It is currently an exciting\\\\nand '\n", + " 'timely research topic to identify how the emerging quantum '\n", + " \"hardware can most', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'2ba64cf8-ce27-4486-8bc8-6dc319c814d7', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': 'Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '15\\\\n\\\\nefficiently be ’put to use’ by laying out '\n", + " 'realistic and resource efficient quantum '\n", + " 'network\\\\narchitectures. Such a research programme '\n", + " 'requests a close interplay between theory and\\\\nexperiment '\n", + " 'in a continuous effort on how to exploit new quantum '\n", + " 'technology.\\\\n\\\\n9. Acknowledgements\\\\n\\\\nIt is a pleasure '\n", + " 'to acknowledge all the excellent colleagues who have '\n", + " 'contributed to my\\\\nunderstanding of solid-state photonic '\n", + " 'quantum networks including A.S. Sørensen, '\n", + " 'R.J.\\\\nWarburton, T.C. Ralph, and A.G. White, as well as '\n", + " 'all past and present members of\\\\nthe Quantum Photonics '\n", + " 'Group at the Niels Bohr Institute, University of '\n", + " 'Copenhagen.\\\\nI thank Nir Rotenberg for proof reading the '\n", + " 'manuscript. I gratefully '\n", + " 'acknowledge\\\\nfinancial support from the European Research '\n", + " 'Council (ERC Advanced Grant SCALE ),\\\\nInnovation Fund '\n", + " 'Denmark (Quantum Innovation Center Qubiz ), and the Danish '\n", + " 'Council\\\\nfor Independent Research.\\\\n [1] H. J. Kimble, '\n", + " 'The quantum internet, Nature 453, 1023 (2008).\\\\n [2] A. '\n", + " 'Reiserer and G. Rempe, Cavity-based quantum networks with '\n", + " 'single atoms and optical photons,\\\\n Rev. Mod. '\n", + " 'Phys. 87, 1379 (2015).\\\\n [3] A.V. Kuhlmann, J. Houel, A. '\n", + " 'Ludwig, L. Greuter, D. Reuter, A.D. Wieck, M. Poggio, and '\n", + " 'R.J.\\\\n Warburton, Charge noise and spin noise in a '\n", + " 'semiconductor quantum device, Nature Phys. 9,\\\\n '\n", + " '570 (2013).\\\\n [4] N. Somaschi, V. Giesz, L. De Santis, J. '\n", + " 'C. Loredo, M. P. Almeida, G. Hornecker, S. L. '\n", + " 'Portalupi,\\\\n T. Grange, C. Anton, J. Demory, C. '\n", + " 'Gomez, I. Sagnes, N. D. Lanzillotti-Kimura, A. Lema '\n", + " '˜Atre,\\\\n A. Auffeves, A. G. White, L. Lanco, and '\n", + " 'P. Senellart, Near-optimal single-photon sources in '\n", + " 'the\\\\n solid state, Nature Phot. 10, 340 (2016).\\\\n '\n", + " '[5] X. Ding, Y. He, Z.-C. Duan, N. Gregersen, M.-C. Chen, '\n", + " 'S. Unsleber, S. Maier, C. Schneider, M.\\\\n Kamp, S. '\n", + " 'Hofling, C.-Y. Lu, and J.-W. Pan, On-demand single photons '\n", + " 'with high extraction\\\\n efficiency and near-unity '\n", + " 'indistinguishability from a resonantly driven quantum dot '\n", + " 'in a\\\\n micropillar, Phys. Rev. Lett. 116, 020401 '\n", + " '(2016).\\\\n [6] A.V. Kuhlmann, J.H. Prechtel, J. Houel, A. '\n", + " 'Ludwig, D. Reuter, A.D. Wieck, and R.J. '\n", + " 'Warburton,\\\\n Transform-limited single photons from '\n", + " 'a single quantum dot, Nature Comm. 6, 8204 (2015)\\\\n [7] '\n", + " 'P. Lodahl, S. Mahmoodian, and S. Stobbe, Interfacing '\n", + " 'single photons and single quantum dots\\\\n with '\n", + " 'photonic nanostructures, Rev. Mod. Phys. 87, 347 '\n", + " '(2015).\\\\n [8] M. Arcari, I. S¨ollner, A. Javadi, S. '\n", + " 'Lindskov Hansen, S. Mahmoodian, J. Liu, H. '\n", + " 'Thyrrestrup,\\\\n E. H. Lee, J. D. Song, S. Stobbe, '\n", + " 'and P. Lodahl, Near-unity coupling efficiency of a '\n", + " 'quantum\\\\n emitter to a photonic crystal waveguide, '\n", + " 'Phys. Rev. Lett. 113, 093603 (2014).\\\\n [9] I. Schwartz, '\n", + " 'D. Cogan, E.R. Schmidgall, Y. Don, L. Gantz, O. Kenneth, '\n", + " 'N.H. Lindner, and D.\\\\n Gershoni, Deterministic '\n", + " 'generation of a cluster state of entangled photons, '\n", + " 'Science 354, 434\\\\n (2016).\\\\n[10] H. Wang, Y. He, '\n", + " 'Y.-H. Li, Z.-E. Su, B. Li, H.-L. Huang, X. Ding, M.-C. '\n", + " 'Chen, C. Liu, J. Qin,\\\\n J.-P. Li, Y.-M. He, C. '\n", + " 'Schneider, M. Kamp, C.-Z. Peng, S. Hoefling, C.-Y. Lu, and '\n", + " 'J.-W. Pan,\\\\n High-efficiency multiphoton boson '\n", + " 'sampling, Nature Phot. 11, 361 (2017).\\\\n[11] C.H.H. '\n", + " 'Schulte, J. Hansom, A.E. Jones, C. Matthiesen, C. Le Gall, '\n", + " 'and M. Atature, Quadrature\\\\n squeezed photons from '\n", + " 'a two-level system, Nature 525, 222 (2015).\\\\n[12] D. '\n", + " 'Huber, M. Reindl, Y. Huo, H. Huang, J.S. Wildmann, O.G. '\n", + " 'Schmidt, A. Rastelli, and R.\\\\n Trotta, Highly '\n", + " 'indistinguishable and strongly entangled photons from '\n", + " 'symmetric GaAs quantum\\\\n dots, Nature Comm. 8, '\n", + " '15506 (2017)\\\\n[13] K. De Greve, P.L. McMahon, D. Press, '\n", + " 'T.D. Ladd, D. Bisping, C. Schneider, M. Kamp, L.\\\\n '\n", + " 'Worschech, S. Hofling, A. Forchel, and Y. Yamamoto, '\n", + " 'Ultrafast coherent control and suppressed\\\\n '\n", + " 'nuclear feedback of a single quantum dot hole qubit, '\n", + " \"Nature Physics 7, 872 (2011).', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'9e6eac23-ac2f-4095-83e3-51fbd271c644', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': ' Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '16\\\\n\\\\n [14] J.H. Prechtel, A.V. Kuhlmann, J. Houel, A. '\n", + " 'Ludwig, S.R. Valentin, A.D. Wieck, and R.J.\\\\n '\n", + " 'Warburton, Decoupling a hole spin qubit from the nuclear '\n", + " 'spins, Nature Materials 15, 981\\\\n (2016)\\\\n[15] '\n", + " 'K. Hammerer, A.S. Sørensen, and E.S. Polzik, Quantum '\n", + " 'interface between light and atomic\\\\n ensembles, '\n", + " 'Rev. Mod. Phys. 82, 1041 (2010).\\\\n [16] C. Santori, D. '\n", + " 'Fattal, J. Vukovic, G.S. Solomon, and Y. Yamamoto, '\n", + " 'Indistinguishable photons from\\\\n a single-photon '\n", + " 'device, Nature 419, 594 (2002).\\\\n[17] P. Michler, A. '\n", + " 'Kiraz, C. Becher, W.V. Schoenfeld, P.M. Petroff, L. Zhang, '\n", + " 'E. Hu, and A. Imamoglu,\\\\n A quantum dot '\n", + " 'single-photon turnstile device, Science 290, 2282 '\n", + " '(2000).\\\\n[18] Y.-M. He, Y. He, Y.-J. Wei, D. Wu, M. '\n", + " 'Atature, C. Schneider, S. Hofling, M. Kamp, C.-Y. Lu, '\n", + " 'and\\\\n J.-W. Pan, On-demand semiconductor '\n", + " 'single-photon source with near-unity '\n", + " 'indistinguishability,\\\\n Nature Nano. 8, 213 '\n", + " '(2013).\\\\n[19] R.J. Warburton, Single spins in '\n", + " 'self-assembled quantum dots, Nature Materials 12, 483 '\n", + " '(2013).\\\\n [20] J. Bleuse, J. Claudon, M. Creasey, N.S. '\n", + " 'Malik, J.-M. Gerard, I. Maksymov, J.-P. Hugonin, '\n", + " 'and\\\\n P. Lalanne Inhibition, enhancement, and '\n", + " 'control of spontaneous emission in photonic '\n", + " 'nanowires\\\\n Phys. Rev. Lett. 106, 103601 '\n", + " '(2011).\\\\n [21] M. Davanco, M.T. Rakher, W. Wegscheider, '\n", + " 'D. Schuh, A. Badolato, and K. Srinivasan, '\n", + " 'Efficient\\\\n quantum dot single photon extraction '\n", + " 'into an optical fiber using a nanophotonic '\n", + " 'directional\\\\n coupler, Appl. Phys. Lett. 99, '\n", + " '121101 (2011).\\\\n [22] T.G. Tiecke, K.P. Nayak, J.D. '\n", + " 'Thompson, T. Peyronel, N.P. de Leon, V. Vuletic, and M.D. '\n", + " 'Lukin,\\\\n Efficient fiber-optical interface for '\n", + " 'nanophotonic devices, Optica 2, 70 (2015).\\\\n [23] R.S. '\n", + " 'Daveau, K.C. Balram, T. Pregnolato, J. Liu, E.H. Lee, J.D. '\n", + " 'Song, V. Verma, R. Mirin, S. Woo\\\\n Nam, L. '\n", + " 'Midolo, S. Stobbe, K. Srinivasan, and P. Lodahl, Efficient '\n", + " 'fiber-coupled single-photon\\\\n source based on '\n", + " 'quantum dots in a photonic-crystal waveguide, Optica 4, '\n", + " '178 (2017).\\\\n [24] A. Kiraz, M. Atature, and A. Imamoglu, '\n", + " 'Quantum-dot single-photon sources: Prospects '\n", + " 'for\\\\n applications in linear optics '\n", + " 'quantum-information processing, Phys. Rev. A 69, 032305 '\n", + " '(2004).\\\\n [25] P. Tighineanu, C.L. Dreessen, C. Flindt, '\n", + " 'P. Lodahl, and A.S. Sørensen, Phonon '\n", + " 'decoherence\\\\n of quantum dots in photonic '\n", + " 'structures: Broadening of the zero-phonon line and the '\n", + " 'role of\\\\n dimensionality, arXiv:1702.04812 '\n", + " '(2017).\\\\n [26] G. Kirsanske, H. Thyrrestrup, R.S. Daveau, '\n", + " 'C.L. Dreessen, T. Pregnolato, L. Midolo, P.\\\\n '\n", + " 'Tighineanu, A. Javadi, S. Stobbe, R. Schott, A. Ludwig, '\n", + " 'A.D. Wieck, S.I Park, J.D. Song,\\\\n A.V. Kuhlmann, '\n", + " 'I. Sollner, M.C. Lobl, R.J. Warburton, and P. Lodahl, '\n", + " 'Indistinguishable and\\\\n efficient single photons '\n", + " 'from a quantum dot in a planar nanobeam waveguide, '\n", + " 'arXiv:1701.08131\\\\n (2017).\\\\n [27] H. Wang, Z.-C. '\n", + " 'Duan, Y.-H. Li, S. Chen, J.-P. Li, Y.-M. He, M.-C. Chen, '\n", + " 'Y. He, X. Ding, C.-Z.\\\\n Peng, C. Schneider, M. '\n", + " 'Kamp, S. Hofling, C.-Y. Lu, and J.-W. Pan, '\n", + " 'Near-transform-limited\\\\n single photons from an '\n", + " 'efficient solid-state quantum '\n", + " 'emitter, Phys. Rev. Lett. 116, '\n", + " '213601\\\\n (2016).\\\\n [28] J.L. O’Brien, A. '\n", + " 'Furusawa, and J. Vuckovic, Photonic quantum technologies, '\n", + " 'Nature Photonics 3,\\\\n 687 (2009).\\\\n [29] J. '\n", + " 'Carolan, C. Harrold, C. Sparrow, E. Martin-Lopez, N.J. '\n", + " 'Russell, J.W. Silverstone, P.J. Shadbolt,\\\\n N. '\n", + " 'Matsuda, M. Oguma, M. Itoh, G.D. Marshall, M.G. Thompson, '\n", + " 'J.C.F. Matthews, T.\\\\n Hashimoto, J.L. O’Brien, '\n", + " 'and A. Laing, Universal linear optics, Science 349, 711 '\n", + " '(2015).\\\\n [30] J. Wang, A. Santamato, P. Jiang, D. '\n", + " 'Bonneau, E. Engin, J.W. Silverstone, M. Lermer, J. Beetz, '\n", + " 'M.\\\\n Kamp, S. Hofling, M.G. Tanner, C.M. '\n", + " 'Natarajan, R.H. Hadfield, S.N. Dorenbos, V. Zwiller, '\n", + " 'J.L.\\\\n O’Brien, and M.G. Thompson, Gallium '\n", + " 'arsenide (GaAs) quantum photonic waveguide '\n", + " 'circuits,\\\\n Opt. Commun. 327, 49 (2014).\\\\n [31] '\n", + " 'L. Midolo et al., in preparation (2017).\\\\n [32] L. '\n", + " 'Midolo, A. Schliesser, and A. Fiore, in preparation '\n", + " '(2017).\\\\n [33] A. Aspuru-Guzik and P. Walther, Photonic '\n", + " 'quantum simulators Nature Physics 8, 285 (2012).\\\\n [34] '\n", + " 'J. Kettler, M. Paul, F. Olbrich, K. Zeuner, M. Jetter, and '\n", + " \"P. Michler, Single-photon and photon', 'text_template': \"\n", + " \"'{metadata_str}\\\\n\\\\n{content}'}, {'class_name': \"\n", + " \"'Document', 'doc_id': \"\n", + " \"'4a9600a2-24a1-4600-9fb2-d10ba3b7c33a', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': ' Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '17\\\\n\\\\n pair emission from MOVPE-grown In(Ga)As '\n", + " 'quantum dots: shifting the emission wavelength\\\\n '\n", + " 'from 1.0 to 1.3 μm Appl. Phys. B 122, 48 (2016).\\\\n [35] '\n", + " 'J.-H. Kim, T. Cai, C.J.K. Richardson, R.P. Leavitt, and E. '\n", + " 'Waks, Two-photon interference from\\\\n a bright '\n", + " 'single-photon source at telecom wavelengths, Optica 3, 577 '\n", + " '(2016).\\\\n [36] B. Kambs, J. Kettler, M. Bock, J.N. '\n", + " 'Becker, C. Arend, A. Lenhard, S.L. Portalupi, M. '\n", + " 'Jetter,\\\\n P. Michler, and C. Becher, Low-noise '\n", + " 'quantum frequency down-conversion of '\n", + " 'indistinguishable\\\\n photons, Opt. Express 24, '\n", + " '22250 (2016).\\\\n [37] R.H. Hadfield, Single-photon '\n", + " 'detectors for optical quantum information applications, '\n", + " 'Nature\\\\n Photonics 3, 696 (2009).\\\\n[38] G. '\n", + " 'Reithmaier, S. Lichtmannecker, T. Reichert, P. Hasch, K. '\n", + " 'Muller, M. Bichler, R. Gross, and J.J.\\\\n Finley, '\n", + " 'On-chip time resolved detection of quantum dot emission '\n", + " 'using integrated superconducting\\\\n single photon '\n", + " 'detectors, Sci. Reports 3, 1901 (2013).\\\\n[39] A. Faraon, '\n", + " 'I. Fushman, D. Englund, N. Stoltz, P. Petroff, and J. '\n", + " 'Vukovic, Coherent generation of\\\\n non-classical '\n", + " 'light on a chip via photon-induced tunnelling and '\n", + " 'blockade, Nature Phys. 4, 859\\\\n (2008).\\\\n[40] A. '\n", + " 'Reinhard, T. Volz, M. Winger, A. Badolato, K.J. Hennessy, '\n", + " 'E.L. Hu, and A. Imamoglu, Strongly\\\\n correlated '\n", + " 'photons on a chip, Nature Phot. 6, 93 (2012).\\\\n[41] M.P. '\n", + " 'Bakker, T. Ruytenberg, W. Loffler, A. Barve, L. Coldren, '\n", + " 'M.P. van Exter, and D.\\\\n Bouwmeester, Quantum dot '\n", + " 'nonlinearity through cavity-enhanced feedback with a '\n", + " 'charge memory\\\\n Phys. Rev. B 91, 241305(R) '\n", + " '(2015)\\\\n[42] A.J. Bennett, J.P. Lee, D.J.P. Ellis, I. '\n", + " 'Farrer, D.A. Ritchie, and A.J. Shields, A '\n", + " 'semiconductor\\\\n photon-sorter Nature Nano. 11, '\n", + " '857 (2016).\\\\n[43] A. Javadi, I. S¨ollner, M. Arcari, S.L. '\n", + " 'Hansen, L. Midolo, S. Mahmoodian, G. Kirsanske, '\n", + " 'T.\\\\n Pregnolato, E.H. Lee, J.D. Song, S. Stobbe, '\n", + " 'and P. Lodahl, Single-photon non-linear optics\\\\n '\n", + " 'with a quantum dot in a waveguide, Nature Comm. 6, 8655 '\n", + " '(2015)\\\\n[44] D. Witthaut, M.D. Lukin, and A.S. Sørensen, '\n", + " 'Photon sorters and QND detectors using single\\\\n '\n", + " 'photon emitters, Europhys. Lett. 97, 50007 (2012).\\\\n[45] '\n", + " 'T.C. Ralph, I. S¨ollner, S. Mahmoodian, A.G. White, and P. '\n", + " 'Lodahl, Photon sorting, efficient Bell\\\\n '\n", + " 'measurements and deterministic CZ gate using a passive '\n", + " 'two-level nonlinearity, Phys. Rev.\\\\n Lett. 114, '\n", + " '173603 (2015).\\\\n[46] D.E. Chang, A.S. Sørensen, E.A. '\n", + " 'Demler, and M.D. Lukin, A single-photon transistor '\n", + " 'using\\\\n nanoscale surface plasmons, Nature Phys. '\n", + " '3, 807 (2007).\\\\n [47] B. Urbaszek, X. Marie, T. Amand, O. '\n", + " 'Krebs, P. Voisin, P. Maletinsky, A. Hogele, and A. '\n", + " 'Imamoglu,\\\\n Nuclear spin physics in quantum dots: '\n", + " 'An optical investigation, Rev. Mod. Phys. 85, 79 '\n", + " '(2013).\\\\n [48] K. Heshami, C. Santori, B. Khanaliloo, C. '\n", + " 'Healey, V.M. Acosta, P.E. Barclay, and C. '\n", + " 'Simon,\\\\n Raman quantum memory based on an '\n", + " 'ensemble of nitrogen-vacancy centers coupled to '\n", + " 'a\\\\n microcavity, Phys. Rev. A 89, 040301(R) '\n", + " '(2014).\\\\n [49] H.M. Meyer, R. Stockill, M. Steiner, C. Le '\n", + " 'Gall, C. Matthiesen, E. Clarke, A. Ludwig, J. '\n", + " 'Reichel,\\\\n M. Atature, and M. Kohl, Direct '\n", + " 'photonic coupling of a semiconductor quantum dot and '\n", + " 'a\\\\n trapped ion, Phys. Rev. Lett. 114, 123001 '\n", + " '(2015).\\\\n [50] W.B. Gao, P. Fallahi, E. Togan, J. '\n", + " 'Miguel-Sanchez, and A. Imamoglu, Observation of '\n", + " 'entanglement\\\\n between a quantum dot spin and a '\n", + " 'single photon, Nature 491, 426 (2012).\\\\n[51] W.B. Gao, P. '\n", + " 'Fallahi, E. Togan, A. Delteil, Y.S. Chin, J. '\n", + " 'Miguel-Sanchez, and A. Imamoglu,\\\\n Quantum '\n", + " 'teleportation from a propagating photon to a solid-state '\n", + " 'spin qubit, Nature Comm. 4,\\\\n 2744 (2013).\\\\n[52] '\n", + " 'A. Delteil, Z. Sun, W.B. Gao, E. Togan, S. Faelt, and A. '\n", + " 'Imamoglu, Generation of heralded\\\\n entanglement '\n", + " 'between distant hole spins, Nature Phys. 12, 218 '\n", + " '(2016).\\\\n[53] P. Lodahl, S. Mahmoodian, S. Stobbe, A. '\n", + " 'Rauschenbeutel, P. Schneeweiss, J. Volz, H. Pichler, '\n", + " 'and\\\\n P. Zoller, Chiral quantum optics, Nature '\n", + " '541, 473 (2017).\\\\n[54] I. S¨ollner, S. Mahmoodian, S. '\n", + " 'Lindskov Hansen, L. Midolo, A. Javadi, G. Kirsanske, T. '\n", + " 'Pregnolato,\\\\n H. El-Ella, E.-H. Lee, J.D. Song, '\n", + " \"S. Stobbe, and P. Lodahl, Deterministic photon-emitter', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'9d6d7ce1-ecb5-4330-ab7f-ae5c4747b61c', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum-dot based photonic quantum \"\n", + " \"networks'}, 'metadata_seperator': '\\\\n', \"\n", + " \"'metadata_template': '{key}: {value}', 'mimetype': \"\n", + " \"'text/plain', 'relationships': {}, 'start_char_idx': None, \"\n", + " \"'text': ' Quantum-dot based photonic quantum \"\n", + " 'networks '\n", + " '18\\\\n coupling in chiral photonic circuits, Nature '\n", + " 'Nano. 10, 775 A (2015).ˆ\\\\n [55] R.J. Coles, D.M. Price, '\n", + " 'J.E. Dixon, B. Royall, E. Clarke, P. Kok, M.S. Skolnick, '\n", + " 'A.M. Fox,\\\\n and M.N. Makhonin, Chirality of '\n", + " 'nanophotonic waveguide with embedded quantum emitter '\n", + " 'for\\\\n unidirectional spin transfer, Nature Comm. '\n", + " '7, 11183 (2016).\\\\n [56] S. Mahmoodian, ˆA P. Lodahl, and '\n", + " 'ˆA A. S. Sørensen, Quantum networks with chiral '\n", + " 'light-matter\\\\n interaction in waveguides, Phys. '\n", + " 'Rev. Lett. 117, 240501 (2016).\\\\n [57] P.W. Fry, I.E. '\n", + " 'Itskevich, D.J. Mowbray, M.S. Skolnick, J.J. Finley, J.A. '\n", + " 'Barker, E.P. O’Reilly, L.R.\\\\n Wilson, I.A. '\n", + " 'Larkin, P.A. Maksym, M. Hopkinson, M. Al-Khafaji, J.P.R. '\n", + " 'David, A.G. Cullis,\\\\n G. Hill, and J.C. Clark, '\n", + " 'Inverted electron-hole alignment in InAs-GaAs '\n", + " 'self-assembled quantum\\\\n dots, Phys. Rev. Lett. '\n", + " '84, 733 (2000).\\\\n [58] F. Ding, R. Singh, J.D. Plumhof, '\n", + " 'T. Zander, V. Krapek, Y.H. Chen, M. Benyoucef, V. Zwiller, '\n", + " 'K.\\\\n Dorr, G. Bester, A. Rastelli, and O.G. '\n", + " 'Schmidt, Tuning the exciton binding energies in '\n", + " 'single\\\\n self-assembled InGaAs/GaAs quantum dots '\n", + " 'by piezoelectric-induced biaxial stress, Phys. '\n", + " 'Rev.\\\\n Lett. 104, 067405 (2010).\\\\n [59] R. '\n", + " 'Stockill, M.J. Stanley, L. Huthmacher, E. Clarke, M. '\n", + " 'Hugues, A.J. Miller, C. Matthiesen, C.\\\\n Le Gall, '\n", + " 'and M. Atature, Phase-tuned entangled state generation '\n", + " 'between distant spin qubits,\\\\n arXiv:1702.03422 '\n", + " '(2017).\\\\n [60] D. Dzsotjan, A.S. Sørensen, and M. '\n", + " 'Fleischhauer, Quantum emitters coupled to surface '\n", + " 'plasmons\\\\n of a nanowire: A Greens function '\n", + " 'approach, Phys. Rev. B 82, 075427 (2010).\\\\n [61] T. '\n", + " 'Ramos, H. Pichler, A.J. Daley, and P. Zoller, Quantum spin '\n", + " 'dimers from chiral dissipation in\\\\n cold-atom '\n", + " 'chains, Phys. Rev. Lett. 113, 237203 (2014).\\\\n[62] K.M. '\n", + " 'Weiss, J.M. Elzerman, Y.L. Delley, J. Miguel-Sanchez, and '\n", + " 'A. Imamoglu, Coherent two-\\\\n electron spin qubits '\n", + " 'in an optically active pair of coupled InGaAs quantum '\n", + " 'dots, Phys. Rev.\\\\n Lett. 109, 107401 '\n", + " '(2012).\\\\n[63] T. Rudolph, Why I am optimistic about the '\n", + " 'silicon-photonic route to quantum computing, '\n", + " 'APL\\\\n Photonics 2, 030901 (2017).\\\\n[64] J.H. '\n", + " 'Shapiro, Single-photon Kerr nonlinearities do not help '\n", + " 'quantum computation, Phys. Rev. A\\\\n 73, 062305 '\n", + " '(2006).\\\\n[65] D. Gottesman and I.L. Chuang, Demonstrating '\n", + " 'the viability of universal quantum computation\\\\n '\n", + " 'using teleportation and single-qubit operations, Nature '\n", + " '402, 390 (1999).\\\\n[66] L.M. Duan and H.J. Kimble, '\n", + " 'Scalable photonic quantum computation through '\n", + " 'cavity-assisted\\\\n[67] K. Koshino, S. Ishizaka, and Y. '\n", + " 'Nakamura, Deterministic photon-photon √SWAP gate using a '\n", + " 'Λinteractions, Phys. Rev. Lett. 92, 127902 '\n", + " '(2004).\\\\n system, Phys. Rev. A 82, 010301(R) '\n", + " '(2010).\\\\n[68] S. Rosenblum, A. Borne, and B. Dayan, '\n", + " 'Analysis of deterministic swapping of photonic and '\n", + " 'atomic\\\\n states through single-photon Raman '\n", + " 'interaction, Phys. Rev. A 95, 033814 (2017).\\\\n[69] N.H. '\n", + " 'Lindner and T. Rudolph, Proposal for pulsed on-demand '\n", + " 'sources of photonic cluster state\\\\n strings, '\n", + " 'Phys. Rev. Lett. 103, 113602 (2009).\\\\n[70] R. '\n", + " 'Raussendorf, D.E. Browne, and H.J. Briegel, '\n", + " 'Measurement-based quantum computation on\\\\n '\n", + " 'cluster states, Phys. Rev. A 68, 022312 (2003).\\\\n[71] '\n", + " 'S.E. Economou, N. Lindner, and T. Rudolph, Optically '\n", + " 'generated 2-dimensional photonic cluster\\\\n state '\n", + " 'from coupled quantum dots, Phys. Rev. Lett. 105, 093601 '\n", + " '(2010).\\\\n[72] H. Pichler, S. Choi, P. Zoller, and M.D. '\n", + " 'Lukin, Photonic tensor networks produced by a '\n", + " 'single\\\\n quantum emitter, arXiv:1702.02119\\\\n '\n", + " '[73] K. T. K. Azuma and H.-K. Lo, All-photonic quantum '\n", + " 'repeaters, Nat. Commun. 6, 6787 (2015).\\\\n [74] D. '\n", + " 'Buterakos, E. Barnes, and S.E. Economou, Deterministic '\n", + " 'generation of all-photonic quantum\\\\n repeaters '\n", + " \"from solid-state emitters, arXiv:1612.03869 (2016).', \"\n", + " \"'text_template': '{metadata_str}\\\\n\\\\n{content}'}]}, \"\n", + " \"{'documents': [{'class_name': 'Document', 'doc_id': \"\n", + " \"'be0e8bc0-0012-40bf-90b8-0e50f67a0433', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'arXiv:quant-ph/0407030v1 \"\n", + " '5 Jul 2004\\\\n\\\\n '\n", + " 'On quantum state of entangled photon '\n", + " 'pairs\\\\n '\n", + " 'Ruo Peng WANG\\\\n '\n", + " 'Department of Physics, Peking University, Beijing 100871, '\n", + " 'P.R.China\\\\n '\n", + " 'Email: '\n", + " 'rpwang@cis.pku.edu.cn\\\\n '\n", + " 'June 18, '\n", + " '2018\\\\n\\\\n '\n", + " 'Abstract\\\\n '\n", + " 'I show that the photon pairs used in experimental tests of '\n", + " 'quantum\\\\n '\n", + " 'non-locality based on Bell’s theorem are not in the '\n", + " 'entangled '\n", + " 'quantum\\\\n '\n", + " 'state. The correct quantum state of the “entangled” photon '\n", + " 'pairs is '\n", + " 'sug-\\\\n '\n", + " 'gested. Two experiments for testing this quantum state are '\n", + " 'proposed.\\\\n '\n", + " 'PACS: 03.65.Ud, '\n", + " '42.50.Dv\\\\n '\n", + " 'Keywords: entangled photon pair, entangled quantum states, '\n", + " 'quantum\\\\n '\n", + " 'non-locality\\\\n\\\\n '\n", + " '1 introduction\\\\n '\n", + " 'Quantum non-locality is a controversial topic of quantum '\n", + " 'theory, and '\n", + " 'entangled\\\\n photon '\n", + " 'pairs played a very important role in experimental tests '\n", + " 'of quantum non-\\\\n '\n", + " 'locality. So far, a number of experimental tests of '\n", + " 'quantum non-locality '\n", + " 'based\\\\n on Bell’s '\n", + " 'theorem [1] have been carried out [2, 3, 4, 5, 6]. In '\n", + " 'these experiments\\\\n '\n", + " 'entangled photon pairs are produced, and the polarization '\n", + " 'correlation between\\\\n '\n", + " 'entangled photon pairs is measured. Experimental results '\n", + " 'obtained in these '\n", + " 'tests\\\\n are in favor '\n", + " 'for quantum non-locality. Although it has been pointed out '\n", + " 'that\\\\n this '\n", + " 'polarization correlation is compatible with local realism '\n", + " '[7], these results\\\\n '\n", + " 'are generally considered as direct evidences for the '\n", + " 'existence of quantum '\n", + " 'non-\\\\n locality, and '\n", + " 'more experiments [8, 9, 10] showing different kind of '\n", + " 'quantum\\\\n non-local '\n", + " 'correlation are carried out. The entangled photon pairs '\n", + " 'are used as\\\\n light '\n", + " 'source in these experiments, and the interpretation of the '\n", + " 'experimental\\\\n '\n", + " 'results as proofs for quantum non-locality is closely '\n", + " 'related to the '\n", + " 'assumption\\\\n that '\n", + " 'these photon pairs are in entangled quantum '\n", + " 'state.\\\\n In this '\n", + " 'letter, I will show that the same polarization correlation '\n", + " 'also exists\\\\n for '\n", + " 'certain un-entangled photon pairs. And on other hand, '\n", + " 'based on the '\n", + " 'consid-\\\\n eration of '\n", + " 'momentum conservation, I will also show that the entangled '\n", + " 'quantum\\\\n state is '\n", + " 'not a correct description for photon pairs used in these '\n", + " 'experimental\\\\n tests '\n", + " 'of quantum non-locality. A correct quantum state for these '\n", + " 'photon pairs is\\\\n '\n", + " 'suggested, and two experiments for testing this quantum '\n", + " 'state and the '\n", + " 'quantum\\\\n '\n", + " 'non-locality are '\n", + " 'proposed.\\\\n\\\\n '\n", + " \"1', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'0e51993f-ad65-4849-aaa5-3718b2edf1c8', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': \"\n", + " \"'2 polarization correlation \"\n", + " 'between un-entangled\\\\n '\n", + " 'photons\\\\nLet’s consider a beam of un-entangled photon '\n", + " 'pairs with each photon pair con-\\\\ntaining a left circle '\n", + " 'polarized photon and a right circle polarized photon. '\n", + " 'Such\\\\na beam of photon pairs can be produced by adjusting '\n", + " 'polarization state of\\\\nthe photon pairs obtained from the '\n", + " 'parametric down conversion with a type-II\\\\ncollinear '\n", + " 'phase matching [11]. The quantum state of this '\n", + " 'un-entangled photon\\\\npairs can be expressed '\n", + " 'as\\\\n '\n", + " '|ψ〉 = 2 1(bv †+ ibh†)(bv †− ibh†)|0〉 = 2 1(bv †bv †+ '\n", + " 'bh†bh†)|0〉, '\n", + " '(1)\\\\nwhere bv† and bh are the creation operator for '\n", + " 'vertically polarized photon and,†\\\\nrespectively, for '\n", + " 'horizontally polarized photon with the wave vector~2. By '\n", + " 'using k\\\\nthe Coulomb gauge, we may describe the optical '\n", + " 'field of these photon pairs by\\\\nthe vector potential A~. '\n", + " 'The positive frequencies part of the vector potential '\n", + " 'is\\\\nrelated to the photon annihilation operators in the '\n", + " 'following '\n", + " 'way\\\\n '\n", + " 'A+ = B(bv~v + bh~h)ei~2 '\n", + " '·~\\\\n '\n", + " '~ e e '\n", + " 'k r (2)\\\\nwhere the constant B is given by '\n", + " '[12]\\\\n '\n", + " 'B =√ μ0ℏc2 '\n", + " '(3)\\\\n '\n", + " '2V ω\\\\nwith μ0 the magnetic permeability of the vacuum, ω '\n", + " 'the angular frequency of\\\\nthe photons.\\\\n '\n", + " 'We divide the beam of un-entangled photon pairs into two '\n", + " 'channels by using\\\\na half reflecting mirror (BS). The '\n", + " 'wave vectors of photons in these two channels\\\\nare~1 and '\n", + " 'k k ~2. A half wave '\n", + " 'plate (W P1) is placed into the channel 1 to introduce\\\\na '\n", + " 'phase difference of π between the vertical and horizontal '\n", + " 'components of the\\\\noptical field, and another half wave '\n", + " 'plate (W P2) is placed into the channel 2\\\\nto swap the '\n", + " 'vertical and horizontal components of the optical field, '\n", + " 'as shown\\\\nin FIG. 1. As no incident photons with the wave '\n", + " 'vector~1 being present, after k\\\\npassing the beam '\n", + " 'splitter and the wave plates, the optical field becomes '\n", + " '[13]\\\\n '\n", + " 'A~ + = √2 (bv~v − bh~h)ei~1 ·~ + √2 (bh~v + '\n", + " 'bv~h)ei~2 ·~r .Be e '\n", + " 'k r B e e k (4)\\\\nThe photons in '\n", + " 'the channel 1 and 2 are polarized by Glan-Thompsom '\n", + " 'linear\\\\npolarization analyzers P1 and P2, respectively, '\n", + " 'before being detected by single\\\\nphoton detectors D1 and '\n", + " 'D2.\\\\n To analyze the polarization '\n", + " 'correlation between photons detected by detec-\\\\ntors D1 '\n", + " 'and D2, we need expressions of optical field at these '\n", + " 'detectors. Up to\\\\na phase factor, we '\n", + " 'have\\\\n '\n", + " 'A+~θ1 = √2 (bv cos θ1 − bh sin '\n", + " 'θ1)~θ1B '\n", + " 'e '\n", + " '(5)\\\\n '\n", + " \"2', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'99eaa171-c4c6-43bb-8b9c-6deeffc5c6fc', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': \"\n", + " \"' \"\n", + " '1 '\n", + " 'D1\\\\n '\n", + " '2\\\\n 1(b bh\\\\n 2 h † †+b bvv † †) '\n", + " '0 1 2 '\n", + " 'WP1 '\n", + " 'θ1\\\\n '\n", + " '1 P1\\\\n '\n", + " '1 '\n", + " 'θ2\\\\n '\n", + " '2 BS '\n", + " '2\\\\n '\n", + " '1 '\n", + " 'WP2\\\\n '\n", + " '2 2 '\n", + " 'P2\\\\n '\n", + " '1 D2\\\\n\\\\nFigure '\n", + " '1: A schematic illustration of the '\n", + " 'experimental setup for testing the\\\\npolarization '\n", + " 'correlation between un-entangled photon pairs. The '\n", + " 'polarization\\\\nstates of optical beams are indicated by '\n", + " 'arrows.\\\\n\\\\nand '\n", + " 'B\\\\n '\n", + " 'A+~θ2 = √2 (bh cos θ2 + bv sin θ2)~θ2 '\n", + " '.e (6)\\\\nIn the '\n", + " 'above expressions, ~θ1 and ~θ2 are vectors of unit of the '\n", + " 'transmissione e\\\\naxes of the analyzer P1, and '\n", + " 'respectively, P2. The relations (5) and (6) can '\n", + " 'be\\\\nrewritten in the following '\n", + " 'form\\\\n '\n", + " 'A+~θ1 = √2 bθ1 ~θ1 , AθB e ~+2 = √2 '\n", + " 'bθ2~θ2B e '\n", + " '(7)\\\\nwhere\\\\n bθ1 = '\n", + " 'bv cos θ1 − bh sin θ1, bθ2 = bh cos θ2 + bv sin '\n", + " 'θ2 '\n", + " '(8)\\\\nare the annihilation operators for the photon '\n", + " 'polarized in the direction ~θ1 withe\\\\nthe wave vector~1 '\n", + " 'and, respectively, for the photon polarized in the '\n", + " 'direction k\\\\n~θ2 with the wave '\n", + " 'vector~2.\\\\ne '\n", + " 'k\\\\n The coincidence counting rate C(θ1, θ2) is '\n", + " 'proportional to the probability\\\\nof annihilating '\n", + " 'simultaneously one photon polarized in the direction ~θ1 '\n", + " 'at thee\\\\ndetector D1 and one photon polarized in the '\n", + " 'direction ~θ2 at the detector D2.e\\\\nThis probability is, '\n", + " 'in its turn, proportional the matrix '\n", + " 'element\\\\n 〈ψ|bθ2 b†1 bθ1bθ2|ψ〉 = 〈ψ|(bv '\n", + " '† †cos θ1 − bh sin '\n", + " 'θ1)(bh cos θ2 + bvsin θ2)† † '\n", + " '†\\\\n '\n", + " 'θ\\\\n '\n", + " '× (bv cos θ1 − bh sin θ1)(bh cos θ2 + bv sin '\n", + " 'θ2)|ψ〉 '\n", + " '(9)\\\\n '\n", + " '= 4 1sin2(θ1 − θ2).\\\\nTherefore we '\n", + " 'have C(θ1, '\n", + " 'θ2) = CM sin2(θ1 − '\n", + " 'θ2), '\n", + " '(10)\\\\n\\\\n '\n", + " \"3', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'f98c627e-47a9-42c3-83bb-6111f9b06525', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'where CM this the maxim \"\n", + " 'counting rate that occurs at θ1 − θ2 = ±π/2. '\n", + " 'This\\\\ncorrelation is just the same as in the case of '\n", + " 'entangled photon pairs [6, 11], and\\\\nthe Bell’s '\n", + " 'inequality is apparently violated also in the above '\n", + " 'discussed experi-\\\\nment. The violation of the Bell’s '\n", + " 'inequality in this case is only apparent, because\\\\na '\n", + " 'condition for obtaining the Bell’s inequality is not '\n", + " 'fulfilled here. Namely, in\\\\nthe experiment described in '\n", + " 'this paper, both photons of a photon pair could\\\\nbe '\n", + " 'detected in one single channel, with the wave vector k~1 '\n", + " 'or k~2. Thus, even\\\\nthough the same polarization '\n", + " 'correlation as in the case of entangled photon\\\\npairs is '\n", + " 'realized in this experiment, no quantum non-locality is '\n", + " 'involved here.\\\\nIndeed, after annihilating of a linear '\n", + " 'polarized photon at the detector D1, the\\\\nquantum state '\n", + " 'becomes\\\\n '\n", + " '|ψ′〉 = bθ1 |ψ〉 = 1√2 (cos θ1b† − sin '\n", + " 'θ1b†)|0〉.v '\n", + " 'h (11)\\\\nThe photon in the quantum '\n", + " 'state |ψ′〉 is not spatially separated from the '\n", + " 'detector\\\\nD1 where another photon was annihilated. The '\n", + " 'probability as the photon in\\\\nthe quantum state |ψ′〉 '\n", + " 'being detected at D1 '\n", + " 'is\\\\n '\n", + " '〈ψ′|bθ1 bθ1 |ψ′〉 = 2 † '\n", + " '1. '\n", + " '(12)\\\\n3 quantum state of entangled '\n", + " 'photon pairs\\\\nNow we arrive at a very important point: '\n", + " 'the observation of a photon polar-\\\\nization correlation '\n", + " 'as expressed in the relation (10) does not form a '\n", + " 'sufficient\\\\nevidence for quantum non-locality. Besides '\n", + " 'this correlation, one must exclude\\\\nthe possibility of '\n", + " 'detecting both of the photons in one single channel to '\n", + " 'ensure\\\\nthat the phenomena of quantum non-locality are '\n", + " 'really observed.\\\\n An apparently non-local '\n", + " 'correlation as expressed in the relation (10) are '\n", + " 'ob-\\\\nserved experimentally for the entangled photon pairs '\n", + " 'generated in the paramet-\\\\nric down conversion nonlinear '\n", + " 'optical processes with a type-II phase matching.\\\\nWe have '\n", + " 'to see if the possibility of detecting both of the photons '\n", + " 'in one single\\\\nchannel can be ruled out in the experiment '\n", + " 'so that one can really interpret the\\\\nexperimental '\n", + " 'results as an experimental evidence for quantum '\n", + " 'non-locality. So\\\\nfar the following quantum state is used '\n", + " 'to describe this photon '\n", + " 'pair[11]:\\\\n '\n", + " '|ψe〉 = 1√2 (bv†1b†2 − bvh '\n", + " '†2b†1)|0〉,h (13)\\\\nwhere bh1, '\n", + " 'b†1, b†2 and bv2 are creation operators for horizontally '\n", + " 'polarized pho-† '\n", + " '†\\\\n '\n", + " 'v h\\\\ntons with the wave vector k~1, for '\n", + " 'vertically polarized photons with the wave\\\\nvector~1, for '\n", + " 'horizontally polarized photons with the wave vector k '\n", + " 'k '\n", + " '~2, and for\\\\nvertically polarized photons with the wave '\n", + " 'vector~2, respectively. By using the k\\\\ncorresponding '\n", + " 'photon annihilation operators, we may write the optical '\n", + " 'field '\n", + " 'as\\\\n A+ '\n", + " '= B(bv1~v + bh1~h)ei~1 ·~ + B(bv2~v + bh2~h)ei~2 '\n", + " '·~.\\\\n '\n", + " '~e '\n", + " 'e e k '\n", + " 'r e e k r '\n", + " '(14)\\\\n\\\\n '\n", + " \"4', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'288c7d8f-ef47-4768-b601-8119e00d4d55', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': 'It is easy to verify that \"\n", + " 'there exists a correlation as described by the '\n", + " 'relation\\\\n(10) between the polarization of the photon '\n", + " 'pairs in the quantum state |ψe〉.\\\\n The '\n", + " 'expression (13) for the quantum state of the entangled '\n", + " 'photon pairs was\\\\nderived by taking into consideration of '\n", + " 'the energy and the momentum conserva-\\\\ntion in the '\n", + " 'parametric down conversion process. The energy and the '\n", + " 'momentum\\\\nof a photon pair in the state |ψe〉 is equal to '\n", + " 'the energy and the momentum of\\\\nthe incident photon from '\n", + " 'which the photon pair is generated. But there is\\\\na '\n", + " 'problem in this argument, namely the parametric down '\n", + " 'conversion process\\\\ntakes place in nonlinear optical '\n", + " 'crystals, so one must take the photons and the\\\\ncrystal '\n", + " 'as one single system when the condition of the energy and '\n", + " 'the momen-\\\\ntum conservation is applied. The macroscopic '\n", + " 'properties of nonlinear optical\\\\ncrystals do not change '\n", + " 'in the parametric down conversion process. Thus '\n", + " 'the\\\\nexpectation values of the energy and the momentum of '\n", + " 'crystals are the same\\\\nbefore and after the photon pairs’ '\n", + " 'generation. Therefore the expectation val-\\\\nues of energy '\n", + " 'and momentum of a photon pair must equal to the '\n", + " 'expectation\\\\nvalues of energy and momentum of incident '\n", + " 'photon. This condition is satisfied\\\\nby the quantum state '\n", + " '|ψe〉. On other hand, photons are coupled with '\n", + " 'optical\\\\nphonons with same momentum when passing through '\n", + " 'crystals, and there exist\\\\nexchanges of energy and '\n", + " 'momentum between photons and crystals through '\n", + " 'the\\\\nphoton-phonon interaction during photons’ '\n", + " 'propagation in crystals. One may\\\\nneglect the energy '\n", + " 'exchange between photons and crystals because the '\n", + " 'ener-\\\\ngies of phonons are much smaller than that of '\n", + " 'photons with same momentum,\\\\nbut the momentum exchange '\n", + " 'between photons and crystals must be taken '\n", + " 'into\\\\nconsideration. This exchange of momentum causes '\n", + " 'fluctuations in momenta of\\\\ncrystals and generated photon '\n", + " 'pairs. But the state |ψe〉 is an eigen-state of '\n", + " 'the\\\\nphoton pairs’ momentum, that means there is not '\n", + " 'momentum fluctuations for\\\\nphoton pairs in the state '\n", + " '|ψe〉. Therefore the generated photon pair can not be\\\\nin '\n", + " 'this quantum state.\\\\n According to the '\n", + " 'calculation made before, the correlation between the '\n", + " 'po-\\\\nlarization of the photon pairs as described by the '\n", + " 'relation (10) does exist also\\\\nfor photon pairs in the '\n", + " 'following quantum '\n", + " 'state\\\\n '\n", + " '|ψu〉 = 2 1(b1 †+ ib2†)(b1 †− ib2†)|0〉 = 2 1(b1†b1 †+ '\n", + " 'b2†b2†)|0〉, '\n", + " '(15)\\\\nwith the optical field given '\n", + " 'by\\\\n '\n", + " 'A~u += √2 (b1~v − b2~h)ei~1 ·~ + √2 (b2~v + b1~h)ei~2 ·~r '\n", + " '.B e e '\n", + " 'k r B e e k '\n", + " '(16)\\\\nThe quantum state |ψu〉 is an un-entangled state. In '\n", + " 'fact, we can write |ψu〉 '\n", + " 'as\\\\n '\n", + " '|ψu〉 = |ψa〉 ⊗ '\n", + " '|ψb〉, '\n", + " '(17)\\\\nwith '\n", + " '†|0〉, |ψb〉 = '\n", + " 'bb†|0〉,\\\\n '\n", + " '|ψa〉 = '\n", + " 'ba '\n", + " '(18)\\\\nwhere '\n", + " '†= '\n", + " '1 '\n", + " '†= '\n", + " '1\\\\n '\n", + " 'ba √2 (b1 †+ ib2†), '\n", + " 'bb '\n", + " '√2 (b1 †− ib2†), [ba†, bb] = 0. '\n", + " '(19)\\\\n '\n", + " \"5', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'43b30d97-ad73-4167-b142-fec2d80f5ce9', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': ' The \"\n", + " 'quantum state |ψu〉 is not an eigen-state of momentum, but '\n", + " 'photon\\\\npairs in the quantum states |ψe〉 and |ψu〉 have '\n", + " 'the same expectation values of\\\\nthe energy and the '\n", + " 'momentum. So the quantum state |ψu〉 satisfies the '\n", + " 'energy\\\\nand momentum conservation requirement. In '\n", + " 'conclusion, the quantum state\\\\n|ψu〉 could be a correct '\n", + " 'description for photon pairs generated in the '\n", + " 'paramet-\\\\nric down conversion nonlinear optical processes '\n", + " 'with a type-II phase matching.\\\\nOne may observe that for '\n", + " 'photon pairs in the quantum state |ψu〉, both '\n", + " 'photons\\\\ncould be detected in one single channel. '\n", + " 'Therefore, by applying the conditions\\\\nof the energy and '\n", + " 'momentum conservation, we can not rule out the '\n", + " 'possibility\\\\nof detecting both photons in one single '\n", + " 'channel. But, instead, we find that the\\\\nphoton pairs can '\n", + " 'not be in the entangled state. The apparently non-local '\n", + " 'cor-\\\\nrelation between the polarization of photon pairs '\n", + " 'produced in the parametric\\\\ndown conversion nonlinear '\n", + " 'optical processes, that were believed being in '\n", + " 'entan-\\\\ngled states, is not a proof for the existence of '\n", + " 'the quantum non-locality, but just\\\\na necessary evidence '\n", + " 'for the fact that photon pairs are in the quantum '\n", + " 'state\\\\n|ψu〉.\\\\n The same conclusion holds '\n", + " 'also for photon pairs emitted in a radiative '\n", + " 'atomic\\\\ncascade of calcium [2, 3]. In that process, '\n", + " 'electrons which emit two photons in a\\\\nradiative cascade '\n", + " 'are well confined within the ions of calcium. The '\n", + " 'uncertainty\\\\nin the momentum of electrons implies that '\n", + " 'the photon pairs can not be in the\\\\nentangled state which '\n", + " 'has a well defined momentum. The quantum state '\n", + " 'for\\\\nphoton pairs generated in this process could be '\n", + " 'expressed '\n", + " 'as\\\\n '\n", + " '|ψu′〉 = 1√2 (bωhb†2h+ bω† 1 ω †1v '\n", + " 'b†2v )|0〉,ω (20)\\\\nwhere bω1h, b†1v are '\n", + " 'creation operators for horizontally, and respectively, '\n", + " 'ver-†ω\\\\ntically polarized photons of circle frequency ω1, '\n", + " 'and bω2h, b†2v are the '\n", + " 'same† '\n", + " 'ω\\\\noperators for photons of circle frequency '\n", + " 'ω2.\\\\n The optical field in the channel 1 '\n", + " 'is given by\\\\n A+= g11(bω1 v~v + '\n", + " 'bωh~h)eicω1~1 ·~ + g12(bω2v~v + '\n", + " 'bω2h~h)eicω2~n1·~r\\\\n '\n", + " '~1 '\n", + " 'e 1 e n '\n", + " 'r e e '\n", + " '(21)\\\\nand\\\\n A+= g21(bω1 v~v + '\n", + " 'bωh~h)eicω1~1 ·~ + g22(bω2v~v + '\n", + " 'bω2h~h)eicω2~n·~r\\\\n '\n", + " '~2 '\n", + " 'e e n '\n", + " 'r e e 1 '\n", + " '(22)\\\\n '\n", + " '1\\\\nin the channel 2, where ~1, ~2 are vectors of unity '\n", + " 'that indicate the '\n", + " 'propaga-n '\n", + " 'n\\\\ntion directions of the channels 1 and 2, and g11, g12, '\n", + " 'g21, g22 are coefficients that\\\\ndepend on the geometry of '\n", + " 'the experiment setup. We can now calculate '\n", + " 'the\\\\nprobability of annihilating simultaneously a photon '\n", + " 'of circle frequency ω1 po-\\\\nlarized in the direction ~θ1 '\n", + " 'in the channel 1 and a photon of circle frequency '\n", + " 'ω2e\\\\npolarized in the direction ~θ2 in the channel 2. By '\n", + " 'using the expressions (20),e\\\\n(21) and (22), we '\n", + " 'find\\\\n '\n", + " 'C12(θ1, θ2) = C′M cos2(θ1 − '\n", + " 'θ2). (23)\\\\nThis '\n", + " 'is exactly the correlation observed in experimental tests '\n", + " 'carried out by\\\\nAspect et al. [2, 3]. Again, no quantum '\n", + " 'non-locality is evolved, and both of the\\\\nphotons can be '\n", + " 'detected in one single channel also in these '\n", + " 'tests.\\\\n\\\\n '\n", + " \"6', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'120d303e-23a4-49c2-8a47-0a738f91d646', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': \"\n", + " \"' \"\n", + " 'D1\\\\n '\n", + " 'θ1\\\\n 1 P1\\\\n '\n", + " '“entangled”\\\\n photon pair 2 '\n", + " 'BS θ4\\\\n '\n", + " '4\\\\n P3 3 '\n", + " 'P4\\\\n θ3 '\n", + " 'D4\\\\n D3\\\\n\\\\nFigure 2: A schematic '\n", + " 'illustration of the experimental setup for testing '\n", + " 'the\\\\npossibility of detecting both photons from one '\n", + " '“entangled” photon pair in one\\\\nsingle channel. The '\n", + " 'polarization states of optical beams are indicated by '\n", + " 'arrows.\\\\n\\\\n4 proposal for experimental '\n", + " 'tests\\\\nDirect experimental tests on the possibility of '\n", + " 'detecting both photons in one\\\\nsingle channel and on the '\n", + " 'coherence of photons in different channels could '\n", + " 'make\\\\nour conclusions on the quantum state of “entangled” '\n", + " 'photons pairs and quan-\\\\ntum non-locality more '\n", + " 'convincing. An experimental test on the possibility '\n", + " 'of\\\\ndetecting both of the photons in one single channel '\n", + " 'can be done by using an\\\\nexperimental setup shown in FIG. '\n", + " '2. This setup is quite similar to that used\\\\nfor tests of '\n", + " 'quantum non-locality based on Bell’s theorem. But a half '\n", + " 'reflecting\\\\nmirror is inserted now into the channel 2 to '\n", + " 'split it into the channels 3 and 4,\\\\nand the coincidence '\n", + " 'counting rate between the channels 3 and 4 is '\n", + " 'measured.\\\\nThis coincidence counting rate is proportional '\n", + " 'to the polarization correlation\\\\nbetween photons in the '\n", + " 'channel 3 and in the channel 4. An '\n", + " 'anti-coincidence\\\\ncondition with the signal from the '\n", + " 'channel 1 can also be applied to ensure that\\\\nthis '\n", + " 'coincidence counting rate is not from other sources. By '\n", + " 'using the expres-\\\\nsions (15), (16), (13) and (14), one '\n", + " 'can easily verify that the following relation\\\\nholds for '\n", + " 'this coincidence counting '\n", + " 'rate\\\\n C(θ3, θ4) = '\n", + " 'CMcos2(θ3 − θ4),′′ (24)\\\\nif the '\n", + " 'photon pairs are in the quantum state |ψu〉, '\n", + " 'and\\\\n\\\\n C(θ3, '\n", + " 'θ4) ≡ 0, (25)\\\\nif the photon '\n", + " 'pairs are in the quantum state |ψe〉.\\\\n The coherence '\n", + " 'of photons in different channels can also be used for '\n", + " 'testing\\\\nthe quantum state of “entangled” photon pairs. '\n", + " 'An experimental setup for such\\\\na test is schematically '\n", + " 'illustrated in FIG. 3. The linear polarizers P1 and P2 '\n", + " 'are\\\\n\\\\n '\n", + " \"7', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'7a329a04-32f6-4bd5-911c-1f787517abfb', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': \"\n", + " \"' \"\n", + " 'M1\\\\n '\n", + " 'P1\\\\n 1 '\n", + " 'WP D\\\\n '\n", + " '“entangled”\\\\n photon pair '\n", + " '2\\\\n '\n", + " '(x,y)\\\\n '\n", + " 'P2\\\\n '\n", + " 'M2\\\\n\\\\nFigure 3: A schematic illustration of the '\n", + " 'experimental setup for testing the\\\\ncoherence between '\n", + " 'photons from one “entangled” photon pair in different '\n", + " 'chan-\\\\nnels. The polarization states of optical beams are '\n", + " 'indicated by arrows.\\\\n\\\\ninserted into the beams of '\n", + " '“entangled” photon pairs, generated in the '\n", + " 'parametric\\\\ndown conversion nonlinear optical processes '\n", + " 'with a type-II phase matching, in\\\\nsuch a way, so that '\n", + " 'the photon in the channel 1 becomes horizontally '\n", + " 'polarized,\\\\nwhile the photon in the channel 2 is '\n", + " 'polarized vertically. The polarization of\\\\nthe beam 1 is '\n", + " 'changed to vertical later by the half wave plate W P . '\n", + " 'Both beams\\\\nare reflected by the mirrors M1 and M2 to '\n", + " 'overlap each other. The single\\\\nphoton counting rate I(x, '\n", + " 'y) in the (x, y) plan as a function of the '\n", + " 'coordinates\\\\n(x, y) is measured by using the single '\n", + " 'photon detector D. Let f1(x, y)~v bee\\\\nthe distribution '\n", + " 'of the vector potential of the beam 1 in the plan (x, y), '\n", + " 'and\\\\nf2(x, y)~v the distribution of the vector potential '\n", + " 'of the beam 2 . If the photone\\\\npair were in the quantum '\n", + " 'state |ψe〉, we have\\\\n '\n", + " 'A+(x, y) = bh1f1(x, y)~v + bv2f2(x, y)~v '\n", + " '.\\\\n '\n", + " '~ e '\n", + " 'e (26)\\\\n The single photon counting rate I(x, y) is '\n", + " 'proportional to the matrix '\n", + " 'element\\\\n I(x, y) ∝ '\n", + " '〈ψe|~+†(x, y) · A+(x, y)|ψe〉. A~ '\n", + " '(27)\\\\nBy using the relation (26) and the expression (13) '\n", + " 'for |ψe〉, we find, for photon\\\\npairs in the quantum state '\n", + " '|ψe〉,\\\\n I(x, y) ∝ '\n", + " '|f1(x, y)|2 + |f2(x, y)|2, (28)\\\\nso no '\n", + " 'interference occurs. But if the photon pairs were in the '\n", + " 'quantum state\\\\n|ψu〉, then according to Eq.(16), we '\n", + " 'have\\\\n A+(x, y) = '\n", + " 'b2f1(x, y)~v + b2f2(x, y)~v '\n", + " '.\\\\n '\n", + " '~ e '\n", + " 'e (29)\\\\nAnd in this case the counting rate '\n", + " 'becomes\\\\n I(x, y) '\n", + " '∝ |f1(x, y) + f2(x, y)|2, (30)\\\\nthat '\n", + " 'means the beam 1 and beam 2 prepared in the way described '\n", + " 'above '\n", + " 'are\\\\ncoherent.\\\\n\\\\n '\n", + " \"8', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'8e86a476-b4c4-4c21-b244-d82f87eca2a4', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': '5 \"\n", + " 'discussion\\\\nI have shown that the correlations of '\n", + " 'photons’ polarization observed in “en-\\\\ntangled” photon '\n", + " 'pairs generated in the parametric down conversion '\n", + " 'nonlinear\\\\noptical processes with a type-II phase '\n", + " 'matching and in a radiative atomic cas-\\\\ncade of calcium '\n", + " 'are not proofs for quantum non-locality. Instead, they '\n", + " 'are\\\\nnecessary evidences for the fact that “entangled” '\n", + " 'photon pairs are in the un-\\\\nentangled states |ψu〉 or '\n", + " '|ψu′〉. According to the expression (16) for the '\n", + " 'vector\\\\npotential, in the case of “entangled” photon '\n", + " 'pairs generated in the parametric\\\\ndown conversion '\n", + " 'nonlinear optical processes with a type-II phase matching, '\n", + " 'we† and b2 †in terms of operators bv1, '\n", + " 'bv† †2, bh1 '\n", + " 'and bh2:† †\\\\nmay express the operators '\n", + " 'b1\\\\n '\n", + " 'b1 = 1† √2 (bv1 + bh2), b2 † '\n", + " '† †= 1√2 (bv2 − bh1).† '\n", + " '† (31)\\\\nWe have '\n", + " 'then\\\\n |ψu〉 = 1√2 |ψe〉 '\n", + " '+ 4 1(bv1b†1 + bv2b†2 + bh1b†1 + '\n", + " 'bh2b†2)|0〉.† v † '\n", + " 'v † h † h '\n", + " '(32)\\\\nAs the component (bv1b†1 + bv2b†2 + bh1b†1 + '\n", + " 'bh2b†2)|0〉 in |ψu〉 has no contri-† '\n", + " '† † '\n", + " '†\\\\n '\n", + " 'v v '\n", + " 'h h\\\\nbution to the coincidence '\n", + " 'counting rate between the signals from the channel 1\\\\nand '\n", + " 'the channel 2, all apparently non-local correlations that '\n", + " 'were believed as\\\\nspecificity of photon pairs in the '\n", + " 'entangled state |ψe〉, occur also in the case '\n", + " 'of\\\\nun-entangled photon pairs in the state |ψu〉. The same '\n", + " 'conclusion holds also\\\\nfor “entangled” photon pairs '\n", + " 'generated in a radiative atomic cascade of cal-\\\\ncium '\n", + " 'Therefore no physical phenomena that necessitate '\n", + " 'introducing quantum\\\\nnon-locality for their explanation '\n", + " 'are really observed.\\\\n Einstein, Podolsky, '\n", + " 'Rosen (EPR) and Bohm had put the completeness of\\\\nquantum '\n", + " 'mechanics in contradiction to the relativistic causality '\n", + " 'by supposing\\\\nthe existence of particle pairs in '\n", + " 'entangled quantum states [14, 15]. But till\\\\nnow, such a '\n", + " 'contradiction did not occur, because no particle pairs in '\n", + " 'entangled\\\\nquantum states had been produced. Can particle '\n", + " 'pairs in entangled quantum\\\\nstates be generated ever? It '\n", + " 'is most likely not. Due to the interaction with\\\\nthe '\n", + " 'source of particle pairs, it should be impossible for the '\n", + " 'produced particle\\\\npair with different momenta to be in a '\n", + " 'quantum state with well defined mo-\\\\nmentum, such as the '\n", + " 'entangled quantum state. This observation is '\n", + " 'consistent\\\\nwith Santos’s suggestion [7] that only '\n", + " 'quantum states which do not contradict\\\\nwith locality '\n", + " 'requirement are physical states. From this point of view, '\n", + " 'the EPR\\\\nparadox is just a spectacular illustration of '\n", + " 'the restriction on quantum states\\\\nimposed by locality '\n", + " 'requirement.\\\\n\\\\nReferences\\\\n[1] J. S. Bell, Physics 1, '\n", + " '195 (1964)\\\\n[2] A. Aspect, P. Grangier, and G. Roger, '\n", + " 'Phys. Rev. Lett. 49, 91 '\n", + " '(1981)\\\\n\\\\n '\n", + " \"9', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}, \"\n", + " \"{'class_name': 'Document', 'doc_id': \"\n", + " \"'170e2b68-f50d-4a3b-8155-79c7b12a3da1', 'embedding': None, \"\n", + " \"'end_char_idx': None, 'excluded_embed_metadata_keys': [], \"\n", + " \"'excluded_llm_metadata_keys': [], 'extra_info': \"\n", + " \"{'file_name': 'Quantum State of Entangled Photon Pairs'}, \"\n", + " \"'metadata_seperator': '\\\\n', 'metadata_template': '{key}: \"\n", + " \"{value}', 'mimetype': 'text/plain', 'relationships': {}, \"\n", + " \"'start_char_idx': None, 'text': '[3] A. Aspect, J. \"\n", + " 'Dalibard, and G. Roger, Phys. Rev. Lett. 49, 1804 '\n", + " '(1981)\\\\n[4] Z. Y. Ou, and L. Mandel, Phys. Rev. Lett. 61, '\n", + " '50 (1988)\\\\n[5] Y. H. Shih, and C. O. Alley, Phys. Rev. '\n", + " 'Lett. 61, 2921 (1988)\\\\n[6] Y. H. Shih, A. V. Sergienko, '\n", + " 'Morton H. Rubin, T. E. Kiess and C. O. Alley,\\\\n Phys. '\n", + " 'Rev. A 50, 23 (1994)\\\\n[7] E. Santos, Phys. Rev. A 46, '\n", + " '3646 (1992)\\\\n[8] D. V. Strekalov, A. V. Sergienko, D. N. '\n", + " 'Klyshko and Y. H. Shih, Phys. Rev.\\\\n Lett. 74, 3600 '\n", + " '(1995)\\\\n[9] E. J. S. Fonseca, P. H. Souto Ribeiro, S. '\n", + " 'P´adua and C. H. Monken, Phys.\\\\n Rev. A 60, 1530 '\n", + " '(1999)\\\\n[10] Z. Zhao, et al., Phys. Rev. Lett. 91, 180401 '\n", + " '(2003)\\\\n[11] P. G. Kwiat et al., Phys. Rev. Lett. 75, '\n", + " '4337 (1995)\\\\n[12] F. Mandl and G. Shaw, Quantum field '\n", + " 'theory (John Wiley and Sons, Chich-\\\\n ester, 1984), '\n", + " 'Chap. 1\\\\n[13] L. Mandel and E. Wolf, Optical Coherence '\n", + " 'and Quantum Optics (Cam-\\\\n bridge University Press, '\n", + " '1995), p. 640\\\\n[14] A. Einstein, B. Pdolsky, and N. '\n", + " 'Rosen, Phys. Rev. 47, 777 (1935)\\\\n[15] D. Bohm, Quantum '\n", + " 'Theory (Prentice Hall, Englewood Cliffs, NJ, '\n", + " '1951)\\\\n\\\\n '\n", + " \"10', 'text_template': '{metadata_str}\\\\n\\\\n{content}'}]}, \"\n", + " \"{'documents': []}]\"}\n" + ] + } + ], + "source": [ + "# get the parsed papers from the llama parse scrape tool\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items[1]\n", + "# pretty printing the research papers\n", + "pprint.pprint(transitions.output)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'arxiv_results': [{'result': [{'authors': ['Seng W. Loke'],\n", + " 'categories': ['cs.ET', 'cs.DC'],\n", + " 'comment': None,\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/2208.00733v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/2208.00733v1',\n", + " 'http://arxiv.org/pdf/2208.00733v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/2208.00733v1',\n", + " 'primary_category': 'cs.ET',\n", + " 'published': '2022-08-01 10:36:13',\n", + " 'summary': 'This article highlights quantum '\n", + " 'Internet computing as referring '\n", + " 'todistributed quantum computing '\n", + " 'over the quantum Internet, '\n", + " 'analogous to(classical) Internet '\n", + " 'computing involving (classical) '\n", + " 'distributed computing overthe '\n", + " '(classical) Internet. Relevant to '\n", + " 'quantum Internet computing would '\n", + " 'be areasof study such as quantum '\n", + " 'protocols for distributed nodes '\n", + " 'using quantuminformation for '\n", + " 'computations, quantum cloud '\n", + " 'computing, delegated '\n", + " 'verifiableblind or private '\n", + " 'computing, non-local gates, and '\n", + " 'distributed quantumapplications, '\n", + " 'over Internet-scale distances.',\n", + " 'title': 'The Rise of Quantum Internet '\n", + " 'Computing',\n", + " 'updated': '2022-08-01 10:36:13'},\n", + " {'authors': ['Seth Lloyd'],\n", + " 'categories': ['quant-ph'],\n", + " 'comment': '9 pages, plain TeX, article from '\n", + " '1998 conference proceedings\\n'\n", + " ' proposing fermionic quantum '\n", + " 'computers',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/quant-ph/0003151v1',\n", + " 'journal_ref': 'in `Unconventional Models of '\n", + " \"Computation,' C.S. Calude, J. \"\n", + " 'Casti,\\n'\n", + " ' M.J. Dinneen, eds., '\n", + " 'Springer, Singapore, 1998',\n", + " 'links': ['http://arxiv.org/abs/quant-ph/0003151v1',\n", + " 'http://arxiv.org/pdf/quant-ph/0003151v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0003151v1',\n", + " 'primary_category': 'quant-ph',\n", + " 'published': '2000-03-31 22:07:23',\n", + " 'summary': 'This paper investigates a variety '\n", + " 'of unconventional quantum '\n", + " 'computationdevices, including '\n", + " 'fermionic quantum computers and '\n", + " 'computers that exploitnonlinear '\n", + " 'quantum mechanics. It is shown '\n", + " 'that unconventional quantum '\n", + " 'computingdevices can in principle '\n", + " 'compute some quantities more '\n", + " \"rapidly than`conventional' quantum \"\n", + " 'computers.',\n", + " 'title': 'Unconventional Quantum Computing '\n", + " 'Devices',\n", + " 'updated': '2000-03-31 22:07:23'}]},\n", + " {'result': [{'authors': ['Shiqi Gong',\n", + " 'Shuaiqiang Liu',\n", + " 'Danny D. Sun'],\n", + " 'categories': ['q-fin.PM'],\n", + " 'comment': '35 pages, 7 figures, 6 tables',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/2306.02764v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/2306.02764v1',\n", + " 'http://arxiv.org/pdf/2306.02764v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/2306.02764v1',\n", + " 'primary_category': 'q-fin.PM',\n", + " 'published': '2023-06-05 10:40:53',\n", + " 'summary': 'Market making plays a crucial role '\n", + " 'in providing liquidity and '\n", + " 'maintainingstability in financial '\n", + " 'markets, making it an essential '\n", + " 'component ofwell-functioning '\n", + " 'capital markets. Despite its '\n", + " 'importance, there is '\n", + " 'limitedresearch on market making '\n", + " 'in the Chinese stock market, which '\n", + " 'is one of thelargest and most '\n", + " 'rapidly growing markets globally. '\n", + " 'To address this gap, weemploy an '\n", + " 'optimal market making framework '\n", + " 'with an exponential '\n", + " 'CARA-type(Constant Absolute Risk '\n", + " 'Aversion) utility function that '\n", + " 'accounts for variousmarket '\n", + " 'conditions, such as price drift, '\n", + " 'volatility, and stamp duty, and '\n", + " 'iscapable of describing 3 major '\n", + " 'risks (i.e., inventory, execution '\n", + " 'and adverseselection risks) in '\n", + " 'market making practice, and '\n", + " 'provide an in-depthquantitative '\n", + " 'and scenario analysis of market '\n", + " 'making in the Chinese stockmarket. '\n", + " 'Our numerical experiments explore '\n", + " 'the impact of volatility on '\n", + " \"themarket maker's inventory. \"\n", + " 'Furthermore, we find that the '\n", + " 'stamp duty rate is acritical '\n", + " 'factor in market making, with a '\n", + " 'negative impact on both the profit '\n", + " 'ofthe market maker and the '\n", + " 'liquidity of the market. '\n", + " 'Additionally, our '\n", + " 'analysisemphasizes the '\n", + " 'significance of accurately '\n", + " 'estimating stock drift for '\n", + " 'managinginventory and adverse '\n", + " 'selection risks effectively and '\n", + " 'enhancing profit for themarket '\n", + " 'maker. These findings offer '\n", + " 'valuable insights for both market '\n", + " 'makers andpolicymakers in the '\n", + " 'Chinese stock market and provide '\n", + " 'directions for furtherresearch in '\n", + " 'designing effective market making '\n", + " 'strategies and policies.',\n", + " 'title': 'Optimal Market Making in the Chinese '\n", + " 'Stock Market: A Stochastic Control '\n", + " 'and Scenario Analysis',\n", + " 'updated': '2023-06-05 10:40:53'},\n", + " {'authors': ['Zhongyang Zhao',\n", + " 'Caisheng Wang',\n", + " 'Huaiwei Liao',\n", + " 'Carol J. Miller'],\n", + " 'categories': ['eess.SY', 'cs.SY'],\n", + " 'comment': '51st North American Power '\n", + " 'Symposium, October 2019',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/1910.14515v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/1910.14515v1',\n", + " 'http://arxiv.org/pdf/1910.14515v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1910.14515v1',\n", + " 'primary_category': 'eess.SY',\n", + " 'published': '2019-10-31 14:55:25',\n", + " 'summary': 'A competitive wholesale '\n", + " 'electricity market consists of '\n", + " 'thousands ofinteracting market '\n", + " 'participants. Driven by the '\n", + " 'variations of fuel costs, '\n", + " 'systemloads and weathers, these '\n", + " 'market participants compete '\n", + " 'actively and behavevariously in '\n", + " 'the power market. Although '\n", + " 'electricity markets tend to become '\n", + " 'moretransparent, a large amount of '\n", + " 'market information is still not '\n", + " 'publiclyavailable to market '\n", + " 'participants. Hence, data-driven '\n", + " 'analysis based on publicdata is '\n", + " 'crucial for market participants to '\n", + " 'better understand and '\n", + " 'modellarge-scale power markets, '\n", + " 'and ultimately to perform better '\n", + " 'in power trading.While most of the '\n", + " 'previous researches related to the '\n", + " 'large-scale power marketsare based '\n", + " 'on the synthetic networks, a '\n", + " 'data-driven approach utilizing the '\n", + " 'realpower market data is proposed '\n", + " 'in this paper. First, the power '\n", + " \"plants' monthlynet generation and \"\n", + " 'capacity data are obtained from '\n", + " 'U.S. Energy '\n", + " 'InformationAdministration (EIA) '\n", + " 'and aggregated to figure out the '\n", + " 'monthly regional capacityfactors '\n", + " 'which are used to characterize the '\n", + " \"market's regional behaviors \"\n", + " 'formarket participants. Then, the '\n", + " 'regional capacity factors are '\n", + " 'analyzed againstthe metered system '\n", + " 'loads and natural gas prices to '\n", + " 'study the generationbehaviors in '\n", + " 'the power market. The analysis '\n", + " 'reveals the impacts of '\n", + " 'regionalnatural gas prices on '\n", + " 'capacity factors and the responses '\n", + " 'of generatingbehaviors to the '\n", + " 'system loads. The analysis results '\n", + " 'present the solid evidenceand '\n", + " 'rational references for market '\n", + " 'participants to model and validate '\n", + " 'thelarge-scale power market in the '\n", + " 'future.',\n", + " 'title': 'Data-driven Analysis of Regional '\n", + " 'Capacity Factors in a Large-Scale '\n", + " 'Power Market: A Perspective from '\n", + " 'Market Participants',\n", + " 'updated': '2019-10-31 14:55:25'}]},\n", + " {'result': [{'authors': ['Peter Lodahl'],\n", + " 'categories': ['quant-ph'],\n", + " 'comment': None,\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/1707.02094v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/1707.02094v1',\n", + " 'http://arxiv.org/pdf/1707.02094v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1707.02094v1',\n", + " 'primary_category': 'quant-ph',\n", + " 'published': '2017-07-07 09:19:15',\n", + " 'summary': 'Quantum dots embedded in photonic '\n", + " 'nanostructures have in recent '\n", + " 'years provento be a very powerful '\n", + " 'solid-state platform for quantum '\n", + " 'optics experiments. Thecombination '\n", + " 'of near-unity radiative coupling '\n", + " 'of a single quantum dot to '\n", + " 'aphotonic mode and the ability to '\n", + " 'eliminate decoherence processes '\n", + " 'imply that anunprecedented '\n", + " 'light-matter interface can be '\n", + " 'obtained. As a '\n", + " 'result,high-cooperativity '\n", + " 'photon-emitter quantum interfaces '\n", + " 'can be constructed openinga '\n", + " 'path-way to deterministic photonic '\n", + " 'quantum gates for '\n", + " 'quantum-informationprocessing '\n", + " 'applications. In the present '\n", + " 'manuscript, I review '\n", + " 'currentstate-of-the-art on quantum '\n", + " 'dot devices and their applications '\n", + " 'for quantumtechnology. The '\n", + " 'overarching long-term goal of the '\n", + " 'research field is toconstruct '\n", + " 'photonic quantum networks where '\n", + " 'remote entanglement can '\n", + " 'bedistributed over long distances '\n", + " 'by photons.',\n", + " 'title': 'Quantum-dot based photonic quantum '\n", + " 'networks',\n", + " 'updated': '2017-07-07 09:19:15'},\n", + " {'authors': ['Ruo Peng Wang'],\n", + " 'categories': ['quant-ph'],\n", + " 'comment': '3 figures',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/quant-ph/0407030v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/quant-ph/0407030v1',\n", + " 'http://arxiv.org/pdf/quant-ph/0407030v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/quant-ph/0407030v1',\n", + " 'primary_category': 'quant-ph',\n", + " 'published': '2004-07-05 08:43:07',\n", + " 'summary': 'I show that the photon pairs used '\n", + " 'in experimental tests of '\n", + " 'quantumnon-locality based on '\n", + " \"Bell's theorem are not in the \"\n", + " 'entangled quantum state.The '\n", + " 'correct quantum state of the '\n", + " \"``entangled'' photon pairs is \"\n", + " 'suggested. Twoexperiments for '\n", + " 'testing this quantum state are '\n", + " 'proposed.',\n", + " 'title': 'Quantum State of Entangled Photon '\n", + " 'Pairs',\n", + " 'updated': '2004-07-05 08:43:07'}]},\n", + " {'result': [{'authors': ['Pierre Paleo',\n", + " 'Alessandro Mirone'],\n", + " 'categories': ['physics.comp-ph', 'cs.CV'],\n", + " 'comment': 'IUCR template, preprint mode, 35 '\n", + " 'figures',\n", + " 'doi': '10.1107/S1600577515010176',\n", + " 'entry_id': 'http://arxiv.org/abs/1502.01480v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://dx.doi.org/10.1107/S1600577515010176',\n", + " 'http://arxiv.org/abs/1502.01480v1',\n", + " 'http://arxiv.org/pdf/1502.01480v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1502.01480v1',\n", + " 'primary_category': 'physics.comp-ph',\n", + " 'published': '2015-02-05 09:51:59',\n", + " 'summary': 'We present a novel approach to '\n", + " 'handle ring artifacts correction '\n", + " 'in compressedsensing tomographic '\n", + " 'reconstruction. The correction is '\n", + " 'part of thereconstruction process, '\n", + " 'which differs from classical '\n", + " 'sinogram pre-processingand image '\n", + " 'post-processing techniques. The '\n", + " 'principle of compressed '\n", + " 'sensingtomographic reconstruction '\n", + " 'is presented. Then, we show that '\n", + " 'the ring artifactscorrection can '\n", + " 'be integrated in the '\n", + " 'reconstruction problem formalism. '\n", + " 'Weprovide numerical results for '\n", + " 'both simulated and real data. This '\n", + " 'technique isincluded in the PyHST2 '\n", + " 'code which is used at the European '\n", + " 'Synchrotron RadiationFacility for '\n", + " 'tomographic reconstruction.',\n", + " 'title': 'Ring artifacts correction in '\n", + " 'compressed sensing tomographic '\n", + " 'reconstruction',\n", + " 'updated': '2015-02-05 09:51:59'},\n", + " {'authors': ['Rasmus Dalgas Kongskov',\n", + " 'Yiqiu Dong'],\n", + " 'categories': ['cs.CE'],\n", + " 'comment': None,\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/1708.06912v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/1708.06912v1',\n", + " 'http://arxiv.org/pdf/1708.06912v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1708.06912v1',\n", + " 'primary_category': 'cs.CE',\n", + " 'published': '2017-08-23 08:08:47',\n", + " 'summary': 'Decomposition of tomographic '\n", + " 'reconstructions has many different '\n", + " 'practicalapplication. We propose '\n", + " 'two new reconstruction methods '\n", + " 'that combines the taskof '\n", + " 'tomographic reconstruction with '\n", + " 'object decomposition. We '\n", + " 'demonstrate thesereconstruction '\n", + " 'methods in the context of '\n", + " 'decomposing directional objects '\n", + " 'intovarious directional '\n", + " 'components. Furthermore we propose '\n", + " 'a method for estimatingthe main '\n", + " 'direction in a directional object, '\n", + " 'directly from the measured '\n", + " 'computedtomography data. We '\n", + " 'demonstrate all the proposed '\n", + " 'methods on simulated and '\n", + " 'realsamples to show their '\n", + " 'practical applicability. The '\n", + " 'numerical tests show '\n", + " 'thatdecomposition and '\n", + " 'reconstruction can combined to '\n", + " 'achieve a highly usefulfibre-crack '\n", + " 'decomposition.',\n", + " 'title': 'Tomographic Reconstruction Methods '\n", + " 'for Decomposing Directional '\n", + " 'Components',\n", + " 'updated': '2017-08-23 08:08:47'}]},\n", + " {'result': [{'authors': ['Liang Liu',\n", + " 'Hongwei Qin',\n", + " 'Jifan Hu'],\n", + " 'categories': ['cond-mat.mes-hall'],\n", + " 'comment': '11 pages, 8 figures',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/1904.04991v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/1904.04991v1',\n", + " 'http://arxiv.org/pdf/1904.04991v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1904.04991v1',\n", + " 'primary_category': 'cond-mat.mes-hall',\n", + " 'published': '2019-04-10 03:37:14',\n", + " 'summary': 'By the means of screened exchange '\n", + " 'density functional theory, we find '\n", + " 'that thephosphorene nanoribbons '\n", + " 'with bare zigzag edges that '\n", + " 'undergo Peierls distortionis a '\n", + " 'antiferromagnetic semiconductor in '\n", + " 'which the polarized states are '\n", + " 'mainlylocalized at the edges. '\n", + " 'Under application of external '\n", + " 'electric fields, thephosphorene '\n", + " 'nanoribbons present exotic '\n", + " 'electronic structure '\n", + " 'transitionsincluding '\n", + " 'semiconducting, half-metallic, and '\n", + " 'metallic states.',\n", + " 'title': 'Exotic Electronic Property of '\n", + " 'Phosphorene Nanoribbons Driven by '\n", + " 'External Electric Field',\n", + " 'updated': '2019-04-10 03:37:14'},\n", + " {'authors': ['Young Jin Kim',\n", + " 'Ping-Han Chu',\n", + " 'Igor Savukov',\n", + " 'Shaun Newman'],\n", + " 'categories': ['hep-ex', 'physics.atom-ph'],\n", + " 'comment': '17 pages, 5 figures',\n", + " 'doi': '10.1038/s41467-019-10169-1',\n", + " 'entry_id': 'http://arxiv.org/abs/1902.00128v2',\n", + " 'journal_ref': None,\n", + " 'links': ['http://dx.doi.org/10.1038/s41467-019-10169-1',\n", + " 'http://arxiv.org/abs/1902.00128v2',\n", + " 'http://arxiv.org/pdf/1902.00128v2'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1902.00128v2',\n", + " 'primary_category': 'hep-ex',\n", + " 'published': '2019-01-31 23:35:06',\n", + " 'summary': 'Exotic spin-dependent interactions '\n", + " 'between fermions have recently '\n", + " 'attractedattention in relation to '\n", + " 'theories beyond the Standard '\n", + " 'Model. The exoticinteractions can '\n", + " 'be mediated by hypothetical '\n", + " 'fundamental bosons which '\n", + " 'mayexplain several unsolved '\n", + " 'mysteries in physics. Here we '\n", + " 'expand this area ofresearch by '\n", + " 'probing an exotic parity-odd spin- '\n", + " 'and velocity-dependentinteraction '\n", + " 'between the axial-vector electron '\n", + " 'coupling and the vector '\n", + " 'nucleoncoupling for polarized '\n", + " 'electrons. This experiment '\n", + " 'utilizes a high-sensitivityatomic '\n", + " 'magnetometer, based on an '\n", + " 'optically polarized vapor that is '\n", + " 'a source ofpolarized electrons, '\n", + " 'and a solid-state mass containing '\n", + " 'unpolarized nucleons.The atomic '\n", + " 'magnetometer can detect an '\n", + " 'effective magnetic field induced '\n", + " 'by theexotic interaction between '\n", + " 'unpolarized nucleons and polarized '\n", + " 'electrons. We setan experimental '\n", + " 'limit on the electron-nucleon '\n", + " 'coupling '\n", + " '$g_\\\\text{A}^\\\\text{e}g_\\\\text{V}^\\\\text{N}<$ '\n", + " '$10^{-30}$ at the mediator boson '\n", + " 'mass below $10^{-4}$ '\n", + " 'eV,significantly improving the '\n", + " 'current limit by up to 17 orders '\n", + " 'of magnitude.',\n", + " 'title': 'Experimental limit on an exotic '\n", + " 'parity-odd spin- and '\n", + " 'velocity-dependent interaction using '\n", + " 'an optically polarized vapor',\n", + " 'updated': '2019-04-24 21:40:25'}]},\n", + " {'result': [{'authors': ['R. Jackiw'],\n", + " 'categories': ['hep-th'],\n", + " 'comment': '13 pages, LaTeX with 2 figures '\n", + " '(available from author), MIT-CTP '\n", + " '#2371',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/hep-th/9410151v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/hep-th/9410151v1',\n", + " 'http://arxiv.org/pdf/hep-th/9410151v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/hep-th/9410151v1',\n", + " 'primary_category': 'hep-th',\n", + " 'published': '1994-10-20 20:21:54',\n", + " 'summary': '25th anniversary and new building '\n", + " 'dedication Centre de '\n", + " \"RecherchesMath\\\\'{e}matiques \"\n", + " \"Montr\\\\'{e}al, Canada, October \"\n", + " '1994',\n", + " 'title': 'My Encounters --- as a Physicist --- '\n", + " 'with Mathematics',\n", + " 'updated': '1994-10-20 20:21:54'},\n", + " {'authors': ['R. Jackiw'],\n", + " 'categories': ['hep-th',\n", + " 'hep-ph',\n", + " 'physics.hist-ph',\n", + " 'quant-ph'],\n", + " 'comment': 'Email correspondence to '\n", + " 'jackiw@mitlns.mit.edu ; 4 pages, '\n", + " 'LaTeX',\n", + " 'doi': '10.1073/pnas.95.22.12776',\n", + " 'entry_id': 'http://arxiv.org/abs/hep-th/9709212v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://dx.doi.org/10.1073/pnas.95.22.12776',\n", + " 'http://arxiv.org/abs/hep-th/9709212v1',\n", + " 'http://arxiv.org/pdf/hep-th/9709212v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/hep-th/9709212v1',\n", + " 'primary_category': 'hep-th',\n", + " 'published': '1997-09-29 20:50:54',\n", + " 'summary': 'The present-day crisis in quantum '\n", + " 'field theory is described.',\n", + " 'title': 'What is quantum field theory and why '\n", + " 'have some physicists abandoned it?',\n", + " 'updated': '1997-09-29 20:50:54'}]},\n", + " {'result': [{'authors': ['Maximilian Russ', 'Guido Burkard'],\n", + " 'categories': ['cond-mat.mes-hall'],\n", + " 'comment': '42 pages, 13 figures',\n", + " 'doi': '10.1088/1361-648X/aa761f',\n", + " 'entry_id': 'http://arxiv.org/abs/1611.09106v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://dx.doi.org/10.1088/1361-648X/aa761f',\n", + " 'http://arxiv.org/abs/1611.09106v1',\n", + " 'http://arxiv.org/pdf/1611.09106v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1611.09106v1',\n", + " 'primary_category': 'cond-mat.mes-hall',\n", + " 'published': '2016-11-28 13:21:43',\n", + " 'summary': 'The goal of this article is to '\n", + " 'review the progress of '\n", + " 'three-electron spinqubits from '\n", + " 'their inception to the state of '\n", + " 'the art. We direct the main '\n", + " 'focustowards the resonant exchange '\n", + " '(RX) qubit and the exchange-only '\n", + " 'qubit, but wealso discuss other '\n", + " 'qubit implementations using three '\n", + " 'electron spins. For eachthree-spin '\n", + " 'qubit we describe the qubit model, '\n", + " 'the physical realization, '\n", + " 'theimplementations of single-qubit '\n", + " 'operations, as well as the '\n", + " 'read-out andinitialization '\n", + " 'schemes. Two-qubit gates and '\n", + " 'decoherence properties '\n", + " 'arediscussed for the RX qubit and '\n", + " 'the exchange-only qubit, thereby, '\n", + " 'completing thelist of requirements '\n", + " 'for a viable candidate qubit '\n", + " 'implementation for '\n", + " 'quantumcomputation. We start with '\n", + " 'describing the full system of '\n", + " 'three electrons in atriple quantum '\n", + " 'dot, then discuss the '\n", + " 'charge-stability diagram and '\n", + " 'restrictourselves to the relevant '\n", + " 'subsystem, introduce the qubit '\n", + " 'states, and discussimportant '\n", + " 'transitions to other charge '\n", + " 'states. Introducing the various '\n", + " 'qubitimplementations, we begin '\n", + " 'with the exchange-only qubit, '\n", + " 'followed by thespin-charge qubit, '\n", + " 'the hybrid qubit, and the RX '\n", + " 'qubit, discussing for each '\n", + " 'thesingle-qubit operations, '\n", + " 'read-out, and initialization '\n", + " 'methods, whereas the mainfocus '\n", + " 'will be on the RX qubit, whose '\n", + " 'single-qubit operations are '\n", + " 'realized bydriving the qubit at '\n", + " 'its resonant frequency in the '\n", + " 'microwave range similar toelectron '\n", + " 'spin resonance. Two different '\n", + " 'types of two-qubit operations '\n", + " 'arepresented for the exchange-only '\n", + " 'and the RX qubit which can be '\n", + " 'divided intoshort-ranged and '\n", + " 'long-ranged interactions. Both of '\n", + " 'these interaction types canbe '\n", + " 'expected to be necessary in a '\n", + " 'large-scale quantum computer. We '\n", + " 'also takeinto account the '\n", + " 'decoherence of the qubit through '\n", + " 'the influence of magneticnoise as '\n", + " 'well as dephasing due to charge '\n", + " 'noise.',\n", + " 'title': 'Three-electron spin qubits',\n", + " 'updated': '2016-11-28 13:21:43'},\n", + " {'authors': ['Sam Young Cho', 'Mun Dae Kim'],\n", + " 'categories': ['cond-mat.supr-con',\n", + " 'cond-mat.mes-hall'],\n", + " 'comment': '5 pages, 1 figure',\n", + " 'doi': '10.1103/PhysRevB.77.212506',\n", + " 'entry_id': 'http://arxiv.org/abs/cond-mat/0703505v1',\n", + " 'journal_ref': 'Phys. Rev. B 77, 212506 (2008)',\n", + " 'links': ['http://dx.doi.org/10.1103/PhysRevB.77.212506',\n", + " 'http://arxiv.org/abs/cond-mat/0703505v1',\n", + " 'http://arxiv.org/pdf/cond-mat/0703505v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/cond-mat/0703505v1',\n", + " 'primary_category': 'cond-mat.supr-con',\n", + " 'published': '2007-03-20 02:20:35',\n", + " 'summary': 'Superconducting flux qubits are '\n", + " 'considered to investigate '\n", + " 'macroscopicmany-qubit '\n", + " 'interactions. Many-qubit states '\n", + " 'based on current states can be '\n", + " 'manipulated through '\n", + " 'thecurrent-phase relation in each '\n", + " 'superconducting loop. For flux '\n", + " 'qubit systems comprised of $N$ '\n", + " 'qubit loops, a general expression '\n", + " 'oflow energy Hamiltonian is '\n", + " 'presented in terms of low energy '\n", + " 'levels of qubits andmacroscopic '\n", + " 'quantum tunnelings between the '\n", + " 'many-qubit states. Many-qubit '\n", + " 'interactions classified by {\\\\em '\n", + " 'Ising type- or '\n", + " 'tunnel-}exchangeinteractions can '\n", + " 'be observable experimentally. '\n", + " 'Flux qubit systems can provide '\n", + " 'various artificial-spin systems to '\n", + " 'studymany-body systems that cannot '\n", + " 'be found naturally.',\n", + " 'title': 'Macroscopic Many-Qubit Interactions '\n", + " 'in Superconducting Flux Qubits',\n", + " 'updated': '2007-03-20 02:20:35'}]},\n", + " {'result': [{'authors': ['Henry Cohn'],\n", + " 'categories': ['math.MG', 'math.NT'],\n", + " 'comment': '24 pages, 9 figures',\n", + " 'doi': '10.1090/noti1474',\n", + " 'entry_id': 'http://arxiv.org/abs/1611.01685v1',\n", + " 'journal_ref': 'Notices Amer. Math. Soc. 64 '\n", + " '(2017), no. 2, 102-115',\n", + " 'links': ['http://dx.doi.org/10.1090/noti1474',\n", + " 'http://arxiv.org/abs/1611.01685v1',\n", + " 'http://arxiv.org/pdf/1611.01685v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1611.01685v1',\n", + " 'primary_category': 'math.MG',\n", + " 'published': '2016-11-05 18:16:28',\n", + " 'summary': 'This expository paper describes '\n", + " \"Viazovska's breakthrough solution \"\n", + " 'of thesphere packing problem in '\n", + " 'eight dimensions, as well as its '\n", + " 'extension totwenty-four dimensions '\n", + " 'by Cohn, Kumar, Miller, Radchenko, '\n", + " 'and Viazovska.',\n", + " 'title': 'A conceptual breakthrough in sphere '\n", + " 'packing',\n", + " 'updated': '2016-11-05 18:16:28'},\n", + " {'authors': ['Jesper W. Schneider',\n", + " 'Rodrigo Costas'],\n", + " 'categories': ['cs.DL'],\n", + " 'comment': 'Accepted for publication in '\n", + " 'Journal of the Association for\\n'\n", + " ' Information Science and '\n", + " 'Technology',\n", + " 'doi': None,\n", + " 'entry_id': 'http://arxiv.org/abs/1512.01388v1',\n", + " 'journal_ref': None,\n", + " 'links': ['http://arxiv.org/abs/1512.01388v1',\n", + " 'http://arxiv.org/pdf/1512.01388v1'],\n", + " 'pdf_downloaded': None,\n", + " 'pdf_url': 'http://arxiv.org/pdf/1512.01388v1',\n", + " 'primary_category': 'cs.DL',\n", + " 'published': '2015-12-04 12:33:07',\n", + " 'summary': 'The article presents three '\n", + " 'advanced citation-based methods '\n", + " 'used to detectpotential '\n", + " 'breakthrough papers among very '\n", + " 'highly cited papers. We approach '\n", + " 'thedetection of such papers from '\n", + " 'three different perspectives in '\n", + " 'order to providedifferent '\n", + " 'typologies of breakthrough papers. '\n", + " 'In all three cases we use '\n", + " 'theclassification of scientific '\n", + " 'publications developed at CWTS '\n", + " 'based on directcitation '\n", + " 'relationships. This classification '\n", + " 'establishes clusters of papers '\n", + " 'atthree levels of aggregation. '\n", + " 'Papers are clustered based on '\n", + " 'their similarcitation orientations '\n", + " 'and it is assumed that they are '\n", + " 'focused on similarresearch '\n", + " 'interests. We use the clustering '\n", + " 'as the context for '\n", + " 'detectingpotential breakthrough '\n", + " 'papers. We utilize the '\n", + " 'Characteristics Scores and '\n", + " 'Scales(CSS) approach to partition '\n", + " 'citation distributions and '\n", + " 'implement a specificfiltering '\n", + " 'algorithm to sort out potential '\n", + " 'highly-cited followers, papers '\n", + " 'notconsidered breakthroughs in '\n", + " 'themselves. After invoking '\n", + " 'thresholds andfiltering, three '\n", + " 'methods are explored: A very '\n", + " 'exclusive one where only '\n", + " 'thehighest cited paper in a '\n", + " 'micro-cluster is considered as a '\n", + " 'potentialbreakthrough paper (M1); '\n", + " 'as well as two conceptually '\n", + " 'different methods, onethat detects '\n", + " 'potential breakthrough papers '\n", + " 'among the two percent highest '\n", + " 'citedpapers according to CSS '\n", + " '(M2a), and finally a more '\n", + " 'restrictive version where, '\n", + " 'inaddition to the CSS two percent '\n", + " 'filter, knowledge diffusion is '\n", + " 'also taken in asan extra parameter '\n", + " '(M2b). The advance citation-based '\n", + " 'methods are explored andevaluated '\n", + " 'using specifically validated '\n", + " 'publication sets linked to '\n", + " 'differentDanish funding '\n", + " 'instruments including centres of '\n", + " 'excellence.',\n", + " 'title': 'Identifying potential breakthrough '\n", + " 'publications using refined citation '\n", + " 'analyses: Three related explorative '\n", + " 'approaches',\n", + " 'updated': '2015-12-04 12:33:07'}]}]}\n" + ] + } + ], + "source": [ + "# arxiv results\n", + "transitions = client.executions.transitions.list(execution_id=execution.id).items[19]\n", + "# pretty printing the arxiv results\n", + "pprint.pprint(transitions.output)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "julep", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cookbooks/08-Managing_Persistent_Sessions.py b/cookbooks/08-Managing_Persistent_Sessions.py deleted file mode 100644 index 40077b7df..000000000 --- a/cookbooks/08-Managing_Persistent_Sessions.py +++ /dev/null @@ -1,137 +0,0 @@ -# Managing Persistent Sessions Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for handling persistent sessions -# 3. Define a task for managing user context -# 4. Create a function to simulate user interactions -# 5. Implement a loop to demonstrate persistent sessions with context management -# 6. Show how to handle context overflow -# 7. Display the session history and context at the end - -import uuid -import yaml -from julep import Client -import time - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for handling persistent sessions -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Session Manager", - about="An AI agent specialized in managing persistent sessions and context.", - model="gpt-4-turbo", -) - -# Defining a task for managing user context -task_def = yaml.safe_load(""" -name: Manage User Context - -input_schema: - type: object - properties: - user_input: - type: string - session_context: - type: object - -main: -- prompt: - role: system - content: >- - You are a session management agent. Your task is to maintain context - across user interactions. Here's the current context: {{inputs[0].session_context}} - - User input: {{inputs[0].user_input}} - - Respond to the user and update the context with any new relevant information. - unwrap: true - -- evaluate: - updated_context: >- - {**inputs[0].session_context, - 'last_interaction': inputs[0].user_input, - 'agent_response': _} - -- return: - response: _ - context: outputs[1].updated_context -""") - -# Creating the task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Function to simulate user interactions -def user_interaction(prompt): - return input(prompt) - -# Create a session -session = client.sessions.create( - agent_id=AGENT_UUID, - context_overflow="adaptive" # Use adaptive context management -) - -# Initialize session context -context = {} - -# Simulate a conversation with persistent context -for i in range(5): - user_input = user_interaction(f"User (Interaction {i+1}): ") - - # Execute the task with user input and current context - execution = client.executions.create( - task_id=TASK_UUID, - input={ - "user_input": user_input, - "session_context": context - } - ) - - # Get the execution result - result = client.executions.get(execution.id) - - # Update the context and print the response - context = result.output['context'] - print(f"Agent: {result.output['response']}") - print(f"Updated Context: {context}") - print() - - # Simulate a delay between interactions - time.sleep(1) - -# Display final session information -print("Final Session Information:") -print(f"Session ID: {session.id}") -print(f"Final Context: {context}") - -# Demonstrate context overflow handling -print("\nDemonstrating Context Overflow Handling:") -large_input = "This is a very large input " * 1000 # Create a large input to trigger overflow -overflow_execution = client.executions.create( - task_id=TASK_UUID, - input={ - "user_input": large_input, - "session_context": context - } -) - -overflow_result = client.executions.get(overflow_execution.id) -print(f"Agent response to large input: {overflow_result.output['response']}") -print(f"Updated context after overflow: {overflow_result.output['context']}") - -# Display session history -print("\nSession History:") -history = client.sessions.messages.list(session_id=session.id) -for message in history.items: - print(f"{message.role}: {message.content}") \ No newline at end of file diff --git a/cookbooks/09-User_Management_and_Personalization.py b/cookbooks/09-User_Management_and_Personalization.py deleted file mode 100644 index 18f9df238..000000000 --- a/cookbooks/09-User_Management_and_Personalization.py +++ /dev/null @@ -1,188 +0,0 @@ -# User Management and Personalization Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for handling user management and personalization -# 3. Define a task for user registration and profile creation -# 4. Define a task for personalized content recommendation -# 5. Create sample users with different preferences -# 6. Demonstrate user registration and profile creation -# 7. Show personalized content recommendations for different users -# 8. Implement a function to update user preferences -# 9. Display updated personalized recommendations after preference changes - -import uuid -import yaml -from julep import Client - -# Global UUIDs for agent and tasks -AGENT_UUID = uuid.uuid4() -REGISTRATION_TASK_UUID = uuid.uuid4() -RECOMMENDATION_TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for user management and personalization -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Personalization Assistant", - about="An AI agent specialized in user management and personalized content recommendations.", - model="gpt-4-turbo", -) - -# Defining a task for user registration and profile creation -registration_task_def = yaml.safe_load(""" -name: User Registration and Profile Creation - -input_schema: - type: object - properties: - username: - type: string - interests: - type: array - items: - type: string - -main: -- prompt: - role: system - content: >- - You are a user registration assistant. Create a user profile based on the following information: - Username: {{inputs[0].username}} - Interests: {{inputs[0].interests}} - - Generate a brief bio and suggest some initial content preferences based on the user's interests. - unwrap: true - -- evaluate: - user_profile: >- - { - "username": inputs[0].username, - "interests": inputs[0].interests, - "bio": _.split('\n\n')[0], - "content_preferences": _.split('\n\n')[1] - } - -- return: outputs[1].user_profile -""") - -# Creating the registration task -registration_task = client.tasks.create_or_update( - task_id=REGISTRATION_TASK_UUID, - agent_id=AGENT_UUID, - **registration_task_def -) - -# Defining a task for personalized content recommendation -recommendation_task_def = yaml.safe_load(""" -name: Personalized Content Recommendation - -input_schema: - type: object - properties: - user_profile: - type: object - -tools: -- name: content_database - type: integration - integration: - provider: mock - setup: - data: [ - {"id": 1, "title": "Introduction to AI", "category": "Technology"}, - {"id": 2, "title": "Healthy Eating Habits", "category": "Health"}, - {"id": 3, "title": "Financial Planning 101", "category": "Finance"}, - {"id": 4, "title": "The Art of Photography", "category": "Art"}, - {"id": 5, "title": "Beginner's Guide to Yoga", "category": "Fitness"} - ] - -main: -- tool: content_database - arguments: {} - -- prompt: - role: system - content: >- - You are a content recommendation system. Based on the user's profile and the available content, - recommend 3 pieces of content that best match the user's interests and preferences. - - User Profile: - {{inputs[0].user_profile}} - - Available Content: - {{outputs[0]}} - - Provide your recommendations in the following format: - 1. [Content ID] - [Content Title] - Reason for recommendation - 2. [Content ID] - [Content Title] - Reason for recommendation - 3. [Content ID] - [Content Title] - Reason for recommendation - unwrap: true - -- return: _ -""") - -# Creating the recommendation task -recommendation_task = client.tasks.create_or_update( - task_id=RECOMMENDATION_TASK_UUID, - agent_id=AGENT_UUID, - **recommendation_task_def -) - -# Function to register a user and create their profile -def register_user(username, interests): - execution = client.executions.create( - task_id=REGISTRATION_TASK_UUID, - input={ - "username": username, - "interests": interests - } - ) - result = client.executions.get(execution.id) - return result.output - -# Function to get personalized content recommendations -def get_recommendations(user_profile): - execution = client.executions.create( - task_id=RECOMMENDATION_TASK_UUID, - input={ - "user_profile": user_profile - } - ) - result = client.executions.get(execution.id) - return result.output - -# Function to update user preferences -def update_user_preferences(user_profile, new_interests): - user_profile["interests"] = list(set(user_profile["interests"] + new_interests)) - return user_profile - -# Demonstrate user registration and personalization -print("Demonstrating User Management and Personalization:") - -# Register users -user1 = register_user("alice", ["technology", "finance"]) -user2 = register_user("bob", ["health", "fitness"]) - -print("\nUser Profiles:") -print(f"Alice: {user1}") -print(f"Bob: {user2}") - -# Get personalized recommendations -print("\nPersonalized Recommendations:") -print("Alice's Recommendations:") -print(get_recommendations(user1)) -print("\nBob's Recommendations:") -print(get_recommendations(user2)) - -# Update user preferences -print("\nUpdating User Preferences:") -updated_alice = update_user_preferences(user1, ["art"]) -print(f"Alice's Updated Profile: {updated_alice}") - -# Get updated recommendations -print("\nUpdated Personalized Recommendations for Alice:") -print(get_recommendations(updated_alice)) \ No newline at end of file diff --git a/cookbooks/10-Document_Management_and_Search.py b/cookbooks/10-Document_Management_and_Search.py deleted file mode 100644 index 87cc492aa..000000000 --- a/cookbooks/10-Document_Management_and_Search.py +++ /dev/null @@ -1,156 +0,0 @@ -# Document Management and Search Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for document management -# 3. Define a task for document upload and indexing -# 4. Define a task for document search -# 5. Create sample documents -# 6. Execute the document upload and indexing task -# 7. Execute the document search task -# 8. Display the search results - -import uuid -import yaml -from julep import Client - -# Global UUID is generated for agent and tasks -AGENT_UUID = uuid.uuid4() -UPLOAD_TASK_UUID = uuid.uuid4() -SEARCH_TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for document management -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Document Manager", - about="An AI agent specialized in document management and search.", - model="gpt-4o", -) - -# Defining a task for document upload and indexing -upload_task_def = yaml.safe_load(""" -name: Document Upload and Indexing - -input_schema: - type: object - properties: - documents: - type: array - items: - type: object - properties: - content: - type: string - metadata: - type: object - -main: -- over: inputs[0].documents - map: - tool: document_upload - arguments: - content: _.content - metadata: _.metadata - -- prompt: - role: system - content: >- - You have successfully uploaded and indexed {{len(outputs[0])}} documents. - Provide a summary of the uploaded documents. -""") - -# Creating the upload task -upload_task = client.tasks.create_or_update( - task_id=UPLOAD_TASK_UUID, - agent_id=AGENT_UUID, - **upload_task_def -) - -# Defining a task for document search -search_task_def = yaml.safe_load(""" -name: Document Search - -input_schema: - type: object - properties: - query: - type: string - filters: - type: object - -main: -- tool: document_search - arguments: - query: inputs[0].query - filters: inputs[0].filters - -- prompt: - role: system - content: >- - Based on the search results, provide a summary of the most relevant documents found. - Search query: {{inputs[0].query}} - Number of results: {{len(outputs[0])}} - - Results: - {{outputs[0]}} -""") - -# Creating the search task -search_task = client.tasks.create_or_update( - task_id=SEARCH_TASK_UUID, - agent_id=AGENT_UUID, - **search_task_def -) - -# Sample documents -sample_documents = [ - { - "content": "Artificial Intelligence (AI) is revolutionizing various industries, including healthcare, finance, and transportation.", - "metadata": {"category": "technology", "author": "John Doe"} - }, - { - "content": "Climate change is a pressing global issue that requires immediate action from governments, businesses, and individuals.", - "metadata": {"category": "environment", "author": "Jane Smith"} - }, - { - "content": "The COVID-19 pandemic has accelerated the adoption of remote work and digital technologies across many organizations.", - "metadata": {"category": "business", "author": "Alice Johnson"} - } -] - -# Execute the document upload and indexing task -upload_execution = client.executions.create( - task_id=UPLOAD_TASK_UUID, - input={"documents": sample_documents} -) - -print("Uploading and indexing documents...") -upload_result = client.executions.get(upload_execution.id) -print(upload_result.output) - -# Execute the document search task -search_execution = client.executions.create( - task_id=SEARCH_TASK_UUID, - input={ - "query": "impact of technology on society", - "filters": {"category": "technology"} - } -) - -print("\nSearching documents...") -search_result = client.executions.get(search_execution.id) -print(search_result.output) - -# Display the search results -print("\nSearch Results:") -for transition in client.executions.transitions.list(execution_id=search_execution.id).items: - if transition.type == "tool_call" and transition.tool == "document_search": - for doc in transition.output: - print(f"- {doc['content']} (Score: {doc['score']})") - -print("\nSearch Summary:") -print(search_result.output) \ No newline at end of file diff --git a/cookbooks/11-Advanced_Chat_Interactions.py b/cookbooks/11-Advanced_Chat_Interactions.py deleted file mode 100644 index 692112be1..000000000 --- a/cookbooks/11-Advanced_Chat_Interactions.py +++ /dev/null @@ -1,177 +0,0 @@ -# Advanced Chat Interactions Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for advanced chat interactions -# 3. Define a task for handling complex conversations with context management -# 4. Implement a function to simulate user input -# 5. Create a chat session and demonstrate advanced interactions: -# a. Multi-turn conversation with context retention -# b. Handling context overflow -# c. Conditional responses based on user input -# d. Integrating external information during the conversation -# 6. Display the chat history and any relevant metrics - -import uuid -import yaml -import os -from julep import Client -import time - -# Global UUIDs for agent and task -AGENT_UUID = uuid.uuid4() -CHAT_TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = os.getenv("JULEP_API_KEY") -if not api_key: - raise ValueError("JULEP_API_KEY not found in environment variables") - -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for advanced chat interactions -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Advanced Chat Assistant", - about="An AI agent capable of handling complex conversations with context management and external integrations.", - model="gpt-4-turbo", -) - -# Add a web search tool to the agent -client.agents.tools.create( - agent_id=AGENT_UUID, - name="web_search", - description="Search the web for information.", - integration={ - "provider": "brave", - "method": "search", - "setup": {"api_key": "YOUR_BRAVE_API_KEY"}, - }, -) - -# Defining a task for handling complex conversations -chat_task_def = yaml.safe_load(""" -name: Advanced Chat Interaction - -input_schema: - type: object - properties: - user_input: - type: string - chat_history: - type: array - items: - type: object - properties: - role: - type: string - content: - type: string - -tools: -- name: weather_api - type: integration - integration: - provider: weather - setup: - api_key: "YOUR_WEATHER_API_KEY" - -main: -- evaluate: - context_length: len(inputs[0].chat_history) - -- if: - condition: _.context_length > 10 - then: - - evaluate: - summarized_history: "Summarize the following chat history: " + str(inputs[0].chat_history[-10:]) - - prompt: - role: system - content: >- - You are an advanced chat assistant. Here's a summary of the recent conversation: - {{outputs[1].summarized_history}} - - Now, respond to the user's latest input: {{inputs[0].user_input}} - else: - - prompt: - role: system - content: >- - You are an advanced chat assistant. Here's the conversation history: - {{inputs[0].chat_history}} - - Now, respond to the user's latest input: {{inputs[0].user_input}} - -- if: - condition: "weather" in inputs[0].user_input.lower() - then: - - tool: weather_api - arguments: - location: "New York" - - prompt: - role: system - content: >- - The user mentioned weather. Here's the current weather information for New York: - {{outputs[3]}} - - Incorporate this information into your response. - -- return: _ -""") - -# Creating the chat task -chat_task = client.tasks.create_or_update( - task_id=CHAT_TASK_UUID, - agent_id=AGENT_UUID, - **chat_task_def -) - -# Function to simulate user input -def get_user_input(): - return input("User: ") - -# Function to display chat history -def display_chat_history(chat_history): - for message in chat_history: - print(f"{message['role'].capitalize()}: {message['content']}") - -# Main chat loop -def run_chat_session(): - chat_history = [] - print("Starting advanced chat session. Type 'exit' to end the conversation.") - - session = client.sessions.create(agent_id=AGENT_UUID) - - while True: - user_input = get_user_input() - if user_input.lower() == 'exit': - break - - chat_history.append({"role": "user", "content": user_input}) - - execution = client.executions.create( - task_id=CHAT_TASK_UUID, - input={ - "user_input": user_input, - "chat_history": chat_history - } - ) - - result = client.executions.get(execution.id) - assistant_response = result.output - - chat_history.append({"role": "assistant", "content": assistant_response}) - print(f"Assistant: {assistant_response}") - - # Simulate a delay for a more natural conversation flow - time.sleep(1) - - print("\nChat session ended. Here's the complete chat history:") - display_chat_history(chat_history) - -# Run the chat session -run_chat_session() - -# Display execution metrics (optional) -print("\nExecution Metrics:") -for transition in client.executions.transitions.list(execution_id=execution.id).items: - print(f"Step: {transition.type}, Duration: {transition.duration_ms}ms") \ No newline at end of file diff --git a/cookbooks/12-Monitoring_Task_Executions.py b/cookbooks/12-Monitoring_Task_Executions.py deleted file mode 100644 index 7e5f576a0..000000000 --- a/cookbooks/12-Monitoring_Task_Executions.py +++ /dev/null @@ -1,160 +0,0 @@ -# Monitoring Task Executions Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for task execution monitoring -# 3. Define a multi-step task that simulates a complex workflow -# 4. Implement functions for: -# a. Starting task execution -# b. Monitoring execution progress -# c. Handling execution status updates -# d. Logging execution metrics -# 5. Execute the task and demonstrate real-time monitoring -# 6. Display execution summary and metrics - -import uuid -import yaml -from julep import Client -import time - -# Global UUIDs for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for task execution monitoring -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Task Execution Monitor", - about="An AI agent designed to monitor and manage complex task executions.", - model="gpt-4-turbo", -) - -# Defining a multi-step task that simulates a complex workflow -task_def = yaml.safe_load(""" -name: Complex Workflow Simulation - -input_schema: - type: object - properties: - project_name: - type: string - data_size: - type: integer - -tools: -- name: data_processor - type: integration - integration: - provider: mock - setup: - processing_time: 5 # Simulated processing time in seconds - -- name: report_generator - type: integration - integration: - provider: mock - setup: - generation_time: 3 # Simulated generation time in seconds - -main: -- prompt: - role: system - content: >- - Initiating project '{{inputs[0].project_name}}' with data size {{inputs[0].data_size}} units. - Prepare for data processing and report generation. - unwrap: true - -- tool: data_processor - arguments: - data_size: inputs[0].data_size - -- evaluate: - processed_data: "Processed " + str(inputs[0].data_size) + " units of data" - -- tool: report_generator - arguments: - data: outputs[2].processed_data - -- prompt: - role: system - content: >- - Project '{{inputs[0].project_name}}' completed. - Data processed: {{outputs[2].processed_data}} - Report generated: {{outputs[3]}} - - Summarize the project results. - unwrap: true - -- return: _ -""") - -# Creating the task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -def start_task_execution(project_name, data_size): - """Start the task execution and return the execution object.""" - execution = client.executions.create( - task_id=TASK_UUID, - input={ - "project_name": project_name, - "data_size": data_size - } - ) - print(f"Task execution started for project '{project_name}'") - return execution - -def monitor_execution_progress(execution_id): - """Monitor the execution progress in real-time.""" - print("Monitoring execution progress:") - for transition in client.executions.transitions.stream(execution_id=execution_id): - print(f"Step: {transition.type}, Status: {transition.status}") - if transition.status == "completed": - print(f" Output: {transition.output}") - elif transition.status == "failed": - print(f" Error: {transition.error}") - time.sleep(1) # Add a small delay to simulate real-time monitoring - -def get_execution_status(execution_id): - """Get the current status of the execution.""" - execution = client.executions.get(execution_id) - return execution.status - -def log_execution_metrics(execution_id): - """Log and display execution metrics.""" - print("\nExecution Metrics:") - transitions = client.executions.transitions.list(execution_id=execution_id).items - total_duration = sum(t.duration_ms for t in transitions) - for transition in transitions: - print(f"Step: {transition.type}, Duration: {transition.duration_ms}ms") - print(f"Total Execution Time: {total_duration}ms") - -# Main execution flow -def run_task_monitoring_demo(): - project_name = "Data Analysis Project" - data_size = 1000 - - print(f"Starting task execution for '{project_name}' with {data_size} units of data") - execution = start_task_execution(project_name, data_size) - - monitor_execution_progress(execution.id) - - final_status = get_execution_status(execution.id) - print(f"\nFinal Execution Status: {final_status}") - - if final_status == "completed": - result = client.executions.get(execution.id) - print("\nExecution Result:") - print(result.output) - - log_execution_metrics(execution.id) - -# Run the task monitoring demo -run_task_monitoring_demo() \ No newline at end of file diff --git a/cookbooks/13-Error_Handling_and_Recovery.py b/cookbooks/13-Error_Handling_and_Recovery.py deleted file mode 100644 index b45732ed5..000000000 --- a/cookbooks/13-Error_Handling_and_Recovery.py +++ /dev/null @@ -1,163 +0,0 @@ -# Error Handling and Recovery Cookbook -# -# Plan: -# 1. Import necessary libraries and set up the Julep client -# 2. Create an agent for error handling demonstration -# 3. Define a task with potential errors and recovery mechanisms -# 4. Execute the task and demonstrate error handling -# 5. Implement a retry mechanism for failed steps -# 6. Show how to log and report errors -# 7. Demonstrate graceful degradation when a step fails - -import uuid -import yaml -import time -from julep import Client - -# Global UUID is generated for agent and task -AGENT_UUID = uuid.uuid4() -TASK_UUID = uuid.uuid4() - -# Creating Julep Client with the API Key -api_key = "" # Your API key here -client = Client(api_key=api_key, environment="dev") - -# Creating an agent for error handling demonstration -agent = client.agents.create_or_update( - agent_id=AGENT_UUID, - name="Error Handler", - about="An AI agent specialized in demonstrating error handling and recovery mechanisms.", - model="gpt-4-turbo", -) - -# Defining a task with potential errors and recovery mechanisms -task_def = yaml.safe_load(""" -name: Error Handling Demo - -input_schema: - type: object - properties: - operation: - type: string - enum: ["divide", "api_call", "process_data"] - value: - type: number - -tools: -- name: divide - type: function - function: - name: divide - description: Divide 100 by the given number - parameters: - type: object - properties: - divisor: - type: number - -- name: api_call - type: integration - integration: - provider: httpbin - method: get - -- name: process_data - type: function - function: - name: process_data - description: Process the given data - parameters: - type: object - properties: - data: - type: string - -main: -- switch: - value: inputs[0].operation - cases: - divide: - - tool: divide - arguments: - divisor: inputs[0].value - on_error: - retry: - max_attempts: 3 - delay: 2 - fallback: - return: "Error: Division by zero or invalid input" - api_call: - - tool: api_call - arguments: - endpoint: "/status/{{inputs[0].value}}" - on_error: - retry: - max_attempts: 3 - delay: 5 - fallback: - return: "Error: API call failed after multiple attempts" - process_data: - - evaluate: - data: "'Sample data: ' + str(inputs[0].value)" - - tool: process_data - arguments: - data: _.data - on_error: - log: "Error occurred while processing data" - return: "Error: Data processing failed" - -- prompt: - role: system - content: >- - Summarize the result of the operation: - Operation: {{inputs[0].operation}} - Result: {{_}} -""") - -# Creating the task -task = client.tasks.create_or_update( - task_id=TASK_UUID, - agent_id=AGENT_UUID, - **task_def -) - -# Function to execute task and handle errors -def execute_task_with_error_handling(operation, value): - try: - execution = client.executions.create( - task_id=TASK_UUID, - input={"operation": operation, "value": value} - ) - - print(f"Executing {operation} with value {value}...") - - # Stream execution to show progress and potential retries - for step in client.executions.transitions.stream(execution_id=execution.id): - if step.type == "tool_call": - print(f"Step: {step.tool}") - if step.status == "error": - print(f"Error occurred: {step.error}") - if step.retry: - print(f"Retrying... (Attempt {step.retry.attempt})") - elif step.type == "error": - print(f"Task error: {step.error}") - - # Get final execution result - result = client.executions.get(execution.id) - print(f"Final result: {result.output}") - - except Exception as e: - print(f"An unexpected error occurred: {str(e)}") - -# Demonstrate error handling for different scenarios -print("1. Division by zero (with retry and fallback):") -execute_task_with_error_handling("divide", 0) - -print("\n2. API call with server error (with retry):") -execute_task_with_error_handling("api_call", 500) - -print("\n3. Data processing error (with logging):") -execute_task_with_error_handling("process_data", "invalid_data") - -print("\n4. Successful operation:") -execute_task_with_error_handling("divide", 4) \ No newline at end of file diff --git a/cookbooks/README.md b/cookbooks/README.md index 7af36aff6..416a35925 100644 --- a/cookbooks/README.md +++ b/cookbooks/README.md @@ -1,5 +1,5 @@ -
- julep +
+ julep
# Julep AI - Notebook Overview @@ -8,21 +8,16 @@ Welcome to the **Julep AI Notebook Collection**! This directory contains a set o Each notebook explores a unique use case, demonstrating different aspects of Julep's capabilities. Below is a quick overview of the notebooks, their purpose, and a link to run each of them on Google Colab. -| **Notebook Name** | **Colab Link** | **Description** | **Implemented** | -|------------------------------------------------- |---------------------------------------------------------------------------- |--------------------------------------------------------------------|-----------------| -| `01-Website_Crawler_using_Spider.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/01-Website_Crawler_using_Spider.ipynb) | Implements a web crawler using a spider to extract website content. | Yes | -| `02-Sarcastic_News_Headline_Generator.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/02-Sarcastic_News_Headline_Generator.ipynb) | Generates sarcastic news headlines using a Brave Search Tool. | Yes | -| `03-SmartResearcher_With_WebSearch.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/03-SmartResearcher_With_WebSearch.ipynb) | Searches and aggregates web information for research purposes using Brave Search. | Yes | -| `04-TripPlanner_With_Weather_And_WikiInfo.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/04-TripPlanner_With_Weather_And_WikiInfo.ipynb) | Plans trips using weather data and Wikipedia information. | Yes | -| `05-Basic_Agent_Creation_and_Interaction.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/05-Basic_Agent_Creation_and_Interaction.ipynb) | Demonstrates how to create a basic agent and interact with it. | No | -| `06-Designing_Multi-Step_Tasks.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/06-Designing_Multi-Step_Tasks.ipynb) | Explores creating tasks with various step types. | No | -| `07-Integrating_External_Tools_and_APIs.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/07-Integrating_External_Tools_and_APIs.ipynb) | Shows how to integrate and use external tools and APIs. | No | -| `08-Managing_Persistent_Sessions.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/08-Managing_Persistent_Sessions.ipynb) | Covers creating and managing persistent sessions with context. | No | -| `09-User_Management_and_Personalization.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/09-User_Management_and_Personalization.ipynb) | Demonstrates user management and personalized interactions. | No | -| `10-Document_Management_and_Search.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/10-Document_Management_and_Search.ipynb) | Explores document upload, management, and search capabilities. | No | -| `11-Advanced_Chat_Interactions.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/11-Advanced_Chat_Interactions.ipynb) | Covers advanced chat features and context handling. | No | -| `12-Monitoring_Task_Executions.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/12-Monitoring_Task_Executions.ipynb) | Shows how to monitor and manage task executions. | No | -| `13-Error_Handling_and_Recovery.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/13-Error_Handling_and_Recovery.ipynb) | Demonstrates implementing error handling and recovery in tasks. | No | +| **Notebook Name** | **Colab Link** | **Description** | **Implemented** | +|------------------ |--------------- |----------------- |----------------- | +| `00-Devfest-Email-Assistant.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/00-Devfest-Email-Assistant.ipynb) | Email assistant for managing DevFest communications | Yes | +| `01-website-crawler.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/01-website-crawler.ipynb) | Implements a web crawler using a spider to extract website content | Yes | +| `02-sarcastic-news-headline-generator.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/02-sarcastic-news-headline-generator.ipynb) | Generates sarcastic news headlines using a Brave Search Tool | Yes | +| `03-trip-planning-assistant.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/03-trip-planning-assistant.ipynb) | Plans trips using weather data and location information | Yes | +| `04-hook-generator-trending-reels.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/04-hook-generator-trending-reels.ipynb) | Generates engaging hooks for trending social media reels | Yes | +| `05-video-processing-with-natural-language.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/05-video-processing-with-natural-language.ipynb) | Processes videos using natural language commands | Yes | +| `06-browser-use.ipynb` | [Colab Link](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/`06-browser-use.ipynb) | Demonstrates browser automation capabilities | Yes | + ## Potential Cookbooks for Contributors @@ -147,4 +142,4 @@ For more details about the tasks or if you have any questions, please don't hesi --- -If you have feedback or would like to contribute to the notebooks, feel free to open an issue(s) in the [repository](https://github.com/julep-ai/julep). \ No newline at end of file +If you have feedback or would like to contribute to the notebooks, feel free to open an issue(s) in the [repository](https://github.com/julep-ai/julep). diff --git a/deploy/simple-docker-compose.yaml b/deploy/simple-docker-compose.yaml new file mode 100644 index 000000000..0b21af407 --- /dev/null +++ b/deploy/simple-docker-compose.yaml @@ -0,0 +1,329 @@ +name: julep + +services: + agents-api: + depends_on: + worker: + condition: service_started + required: true + environment: + AGENTS_API_HOSTNAME: localhost + AGENTS_API_KEY: ${AGENTS_API_KEY} + AGENTS_API_KEY_HEADER_NAME: Authorization + AGENTS_API_PROTOCOL: http + AGENTS_API_PUBLIC_PORT: "80" + AGENTS_API_URL: http://agents-api:8080 + COZO_AUTH_TOKEN: ${COZO_AUTH_TOKEN} + COZO_HOST: http://memory-store:9070 + EMBEDDING_MODEL_ID: voyage/voyage-3 + INTEGRATION_SERVICE_URL: http://integrations:8000 + LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY} + LITELLM_URL: http://litellm:4000 + SUMMARIZATION_MODEL_NAME: gpt-4o-mini + TEMPORAL_ENDPOINT: temporal:7233 + TEMPORAL_NAMESPACE: default + TEMPORAL_TASK_QUEUE: julep-task-queue + TEMPORAL_WORKER_URL: temporal:7233 + TRUNCATE_EMBED_TEXT: "True" + WORKER_URL: temporal:7233 + image: julepai/agents-api:${TAG:-dev} + networks: + default: null + ports: + - mode: ingress + target: 8080 + published: "8080" + protocol: tcp + + cozo-migrate: + environment: + AGENTS_API_HOSTNAME: localhost + AGENTS_API_KEY: ${AGENTS_API_KEY} + AGENTS_API_KEY_HEADER_NAME: Authorization + AGENTS_API_PROTOCOL: http + AGENTS_API_PUBLIC_PORT: "80" + AGENTS_API_URL: http://agents-api:8080 + COZO_AUTH_TOKEN: ${COZO_AUTH_TOKEN} + COZO_HOST: http://memory-store:9070 + EMBEDDING_MODEL_ID: voyage/voyage-3 + INTEGRATION_SERVICE_URL: http://integrations:8000 + LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY} + LITELLM_URL: http://litellm:4000 + SUMMARIZATION_MODEL_NAME: gpt-4o-mini + TEMPORAL_ENDPOINT: temporal:7233 + TEMPORAL_NAMESPACE: default + TEMPORAL_TASK_QUEUE: julep-task-queue + TEMPORAL_WORKER_URL: temporal:7233 + TRUNCATE_EMBED_TEXT: "True" + WORKER_URL: temporal:7233 + image: julepai/cozo-migrate:${TAG:-dev} + networks: + default: null + restart: "no" + + integrations: + image: julepai/integrations:${TAG:-dev} + environment: + OPENAI_API_KEY: ${OPENAI_API_KEY} + networks: + default: null + ports: + - mode: ingress + target: 8000 + published: "8000" + protocol: tcp + + litellm: + command: + - --config + - /app/config.yaml + - --port + - "4000" + - --num_workers + - "8" + - --telemetry + - "False" + depends_on: + litellm-db: + condition: service_started + required: true + litellm-redis: + condition: service_started + required: true + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + CLOUDFLARE_ACCOUNT_ID: ${CLOUDFLARE_ACCOUNT_ID} + CLOUDFLARE_API_KEY: ${CLOUDFLARE_API_KEY} + DATABASE_URL: ${DATABASE_URL} + GITHUB_API_KEY: ${GITHUB_API_KEY} + GOOGLE_APPLICATION_CREDENTIALS: ${GOOGLE_APPLICATION_CREDENTIALS} + GROQ_API_KEY: ${GROQ_API_KEY} + LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY} + NVIDIA_NIM_API_KEY: ${NVIDIA_NIM_API_KEY} + OPENAI_API_KEY: ${OPENAI_API_KEY} + REDIS_URL: redis://default:${REDIS_PASSWORD:-redis}@litellm-redis:6379 + VOYAGE_API_KEY: ${VOYAGE_API_KEY} + hostname: litellm + image: ghcr.io/berriai/litellm-database:main-v1.46.6 + networks: + default: null + restart: unless-stopped + volumes: + - type: bind + source: ../llm-proxy/litellm-config.yaml + target: /app/config.yaml + bind: + create_host_path: true + - type: bind + source: ../llm-proxy/.keys + target: /app/.keys + read_only: true + bind: + create_host_path: true + + litellm-db: + environment: + POSTGRES_DB: ${LITELM_POSTGRES_DB:-litellm} + POSTGRES_PASSWORD: ${LITELM_POSTGRES_PASSWORD:-postgres} + POSTGRES_USER: ${LITELM_POSTGRES_USER:-llmproxy} + healthcheck: + test: + - CMD-SHELL + - pg_isready -d ${LITELM_POSTGRES_DB:-litellm} -U ${LITELM_POSTGRES_USER:-llmproxy} + timeout: 5s + interval: 1s + retries: 10 + image: postgres:16 + networks: + default: null + restart: unless-stopped + volumes: + - type: volume + source: litellm-db-data + target: /var/lib/postgresql/data + volume: {} + + litellm-redis: + environment: + REDIS_ARGS: --requirepass ${REDIS_PASSWORD:-redis} + image: redis/redis-stack-server + networks: + default: null + restart: unless-stopped + volumes: + - type: volume + source: litellm-redis-data + target: /data + volume: {} + + memory-store: + environment: + COZO_AUTH_TOKEN: ${COZO_AUTH_TOKEN} + COZO_BACKUP_DIR: /backup + COZO_MNT_DIR: /data + COZO_PORT: "9070" + image: julepai/memory-store:${TAG:-dev} + labels: + ofelia.enabled: "true" + ofelia.job-exec.backupcron.command: bash /app/backup.sh + ofelia.job-exec.backupcron.environment: '["COZO_PORT=9070", "COZO_AUTH_TOKEN=${COZO_AUTH_TOKEN}", "COZO_BACKUP_DIR=/backup"]' + ofelia.job-exec.backupcron.schedule: '@every 3h' + networks: + default: null + ports: + - mode: ingress + target: 9070 + published: "9070" + protocol: tcp + volumes: + - type: volume + source: cozo_data + target: /data + volume: {} + - type: volume + source: cozo_backup + target: /backup + volume: {} + + memory-store-backup-cron: + command: + - daemon + - --docker + - -f + - label=com.docker.compose.project=julep + depends_on: + memory-store: + condition: service_started + required: true + image: mcuadros/ofelia:latest + networks: + default: null + restart: unless-stopped + volumes: + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + read_only: true + bind: + create_host_path: true + + temporal: + depends_on: + temporal-db: + condition: service_started + required: true + environment: + DB: postgres12 + DB_HOST: temporal-db + DB_PORT: "5432" + DYNAMIC_CONFIG_FILE_PATH: config/dynamicconfig/temporal-postgres.yaml + LOG_LEVEL: info + POSTGRES_DB: ${TEMPORAL_POSTGRES_DB:-temporal} + POSTGRES_PWD: ${TEMPORAL_POSTGRES_PASSWORD:-temporal} + POSTGRES_SEEDS: temporal-db + POSTGRES_TLS_CA_FILE: /cert/ca.crt + POSTGRES_TLS_DISABLE_HOST_VERIFICATION: "false" + POSTGRES_TLS_ENABLED: "false" + POSTGRES_USER: ${TEMPORAL_POSTGRES_USER:-temporal} + SKIP_DB_CREATE: "false" + SKIP_SCHEMA_SETUP: "false" + SQL_CA: /cert/ca.crt + SQL_TLS_ENABLED: "false" + TEMPORAL_ADDRESS: temporal:7233 + VISIBILITY_DBNAME: temporal_visibility + hostname: temporal + image: temporalio/auto-setup:1.25 + networks: + default: null + volumes: + - type: bind + source: ../scheduler/dynamicconfig + target: /etc/temporal/config/dynamicconfig + bind: + create_host_path: true + - type: bind + source: ../scheduler/cert + target: /cert + bind: + create_host_path: true + + temporal-db: + environment: + POSTGRES_DB: ${TEMPORAL_POSTGRES_DB:-temporal} + POSTGRES_PASSWORD: ${TEMPORAL_POSTGRES_PASSWORD:-temporal} + POSTGRES_USER: ${TEMPORAL_POSTGRES_USER:-temporal} + healthcheck: + test: + - CMD-SHELL + - pg_isready -d ${TEMPORAL_POSTGRES_DB:-temporal} -U ${TEMPORAL_POSTGRES_USER:-temporal} + timeout: 5s + interval: 1s + retries: 10 + image: postgres:16 + networks: + default: null + restart: unless-stopped + volumes: + - type: volume + source: temporal-db-data + target: /var/lib/postgresql/data + volume: {} + + temporal-ui: + environment: + TEMPORAL_ADDRESS: temporal:7233 + TEMPORAL_CODEC_ENDPOINT: http://localhost/api/temporal + TEMPORAL_CSRF_COOKIE_INSECURE: "true" + TEMPORAL_FEEDBACK_URL: https://github.com/julep-ai/julep + TEMPORAL_NOTIFY_ON_NEW_VERSION: "false" + TEMPORAL_OPEN_API_ENABLED: "true" + TEMPORAL_UI_ENABLED: "true" + image: temporalio/ui:latest + networks: + default: null + ports: + - mode: ingress + target: 8080 + published: "9000" + protocol: tcp + + worker: + environment: + AGENTS_API_HOSTNAME: localhost + AGENTS_API_KEY: ${AGENTS_API_KEY} + AGENTS_API_KEY_HEADER_NAME: Authorization + AGENTS_API_PROTOCOL: http + AGENTS_API_PUBLIC_PORT: "80" + AGENTS_API_URL: http://agents-api:8080 + COZO_AUTH_TOKEN: ${COZO_AUTH_TOKEN} + COZO_HOST: http://memory-store:9070 + EMBEDDING_MODEL_ID: voyage/voyage-3 + INTEGRATION_SERVICE_URL: http://integrations:8000 + LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY} + LITELLM_URL: http://litellm:4000 + SUMMARIZATION_MODEL_NAME: gpt-4o-mini + TEMPORAL_ENDPOINT: temporal:7233 + TEMPORAL_NAMESPACE: default + TEMPORAL_TASK_QUEUE: julep-task-queue + TEMPORAL_WORKER_URL: temporal:7233 + TRUNCATE_EMBED_TEXT: "True" + WORKER_URL: temporal:7233 + image: julepai/worker:${TAG:-dev} + networks: + default: null + +networks: + default: + name: julep_default + +volumes: + cozo_backup: + name: cozo_backup + cozo_data: + name: cozo_data + litellm-db-data: + name: julep_litellm-db-data + litellm-redis-data: + name: julep_litellm-redis-data + temporal-db-data: + name: julep_temporal-db-data diff --git a/docker-bake.hcl b/docker-bake.hcl index 70d8ed1ea..7b326a437 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -14,6 +14,8 @@ group "default" { "memory-store", "integrations", "gateway", + "blob-store", + "code-interpreter", ] } @@ -69,4 +71,22 @@ target "gateway" { "julepai/gateway:${TAG}", "julepai/gateway:git-${GIT_SHA}" ] -} \ No newline at end of file +} + +target "blob-store" { + context = "./blob-store" + dockerfile = "Dockerfile" + tags = [ + "julepai/blob-store:${TAG}", + "julepai/blob-store:git-${GIT_SHA}" + ] +} + +target "code-interpreter" { + context = "./code-interpreter/vendor/cohere-ai/cohere-terrarium" + dockerfile = "Dockerfile" + tags = [ + "julepai/code-interpreter:${TAG}", + "julepai/code-interpreter:git-${GIT_SHA}" + ] +} diff --git a/docker-compose.yml b/docker-compose.yml index c5ea3bbf0..6488053a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,9 @@ include: - ./scheduler/docker-compose.yml - ./llm-proxy/docker-compose.yml - ./integrations-service/docker-compose.yml + - ./monitoring/docker-compose.yml + - ./blob-store/docker-compose.yml + - ./code-interpreter/docker-compose.yml # TODO: Enable after testing # - ./monitoring/docker-compose.yml @@ -21,7 +24,7 @@ include: # - agents-api runs under two modes: # + single-tenant: `SKIP_CHECK_DEVELOPER_HEADERS=True` and `AGENTS_API_KEY` is required # [user] --{Authorization: Bearer $api-key}--> [agents-api] -# + multi-tenant: +# + multi-tenant: # `SKIP_CHECK_DEVELOPER_HEADERS=False` and requests must have a valid `X-Developer-Id` header # [user] # --{Authorization: Bearer $JWT}--> [gateway] diff --git a/docs/README.md b/docs/README.md index 6b030fc50..8357d7870 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,267 +1,322 @@ ---- -description: Introducing Julep -layout: - title: - visible: true - description: - visible: false - tableOfContents: - visible: true - outline: - visible: false - pagination: - visible: true +English | [中文翻译](https://github.com/julep-ai/julep/blob/dev/README-CN.md) | [日本語翻訳](https://github.com/julep-ai/julep/blob/dev/README-JP.md) + +
+ julep +
+ +

+
+ Explore Docs + · + Discord + · + 𝕏 + · + LinkedIn +

+ +

+ NPM Version +   + PyPI - Version +   + Docker Image Version +   + GitHub License +

+ --- -***** -> ### This docs site is currently under construction although this github README below should suffice for now. +> [!NOTE] +> 👨‍💻 Here for the devfest.ai event? Join our [Discord](https://discord.com/invite/JTSBGRZrzj) and check out the details below. -![](https://i.giphy.com/vR1dPIYzQmkRzLZk2w.webp) -***** +
+🌟 Contributors and DevFest.AI Participants (Click to expand) -## Introduction +## 🌟 Call for Contributors! -Julep is an open-source platform for creating persistent AI agents with customizable workflows. It provides tools to develop, manage, and deploy AI-driven applications, focusing on flexibility and ease of use. +We're excited to welcome new contributors to the Julep project! We've created several "good first issues" to help you get started. Here's how you can contribute: -With Julep, you can: -- Quickly develop AI agents that retain context and state across interactions -- Design and execute sophisticated workflows tailored to your AI agents -- Seamlessly integrate various tools and APIs into your AI workflows -- Effortlessly manage persistent sessions and user interactions +1. Check out our [CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/CONTRIBUTING.md) file for guidelines on how to contribute. +2. Browse our [good first issues](https://github.com/julep-ai/julep/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to find a task that interests you. +3. If you have any questions or need help, don't hesitate to reach out on our [Discord](https://discord.com/invite/JTSBGRZrzj) channel. -Whether you're developing a chatbot, automating tasks, or building a complex AI assistant, Julep provides the flexibility and features you need to turn your ideas into reality swiftly and efficiently. +Your contributions, big or small, are valuable to us. Let's build something amazing together! 🚀 - +### 🎉 DevFest.AI October 2024 -
-Here's a quick python example: +Exciting news! We're participating in DevFest.AI throughout October 2024! 🗓️ - +- Contribute to Julep during this event and get a chance to win awesome Julep merch and swag! 🎁 +- Join developers from around the world in contributing to AI repositories and participating in amazing events. +- A big thank you to DevFest.AI for organizing this fantastic initiative! -

-from julep import Julep, AsyncJulep
+> [!TIP]
+> Ready to join the fun? **[Tweet that you are participating](https://twitter.com/intent/tweet?text=Pumped%20to%20be%20participating%20in%20%40devfestai%20with%20%40julep_ai%20building%20%23ai%20%23agents%20%23workflows%20Let's%20gooo!%20https%3A%2F%2Fgit.new%2Fjulep)** and let's get coding! 🖥️
 
-# 🔑 Initialize the Julep client
-#     Or alternatively, use AsyncJulep for async operations
-client = Julep(api_key="your_api_key")
+> [!NOTE]
+> Get your API key [here](https://dashboard-dev.julep.ai).
+>
+> While we are in beta, you can also reach out on [Discord](https://discord.com/invite/JTSBGRZrzj) to get rate limits lifted on your API key.
 
-##################
-## 🤖 Agent 🤖 ##
-##################
+![Julep DevFest.AI](https://media.giphy.com/media/YjyUeyotft6epaMHtU/giphy.gif)
 
-# Create a research agent
-agent = client.agents.create(
-    name="Research Agent",
-    model="claude-3.5-sonnet",
-    about="You are a research agent designed to handle research inquiries.",
-)
-
-# 🔍 Add a web search tool to the agent
-client.agents.tools.create(
-    agent_id=agent.id,
-    name="web_search",  # Should be python valid variable name
-    description="Use this tool to research inquiries.",
-    integration={
-        "provider": "brave",
-        "method": "search",
-        "setup": {
-            "api_key": "your_brave_api_key",
-        },
-    },
-)
+
-################# -## 💬 Chat 💬 ## -################# + + +
+

📖 Table of Contents

+ +- [🌟 Call for Contributors!](#-call-for-contributors) + - [🎉 DevFest.AI October 2024](#-devfestai-october-2024) +- [Introduction](#introduction) +- [Quick Example](#quick-example) +- [Key Features](#key-features) +- [Why Julep vs. LangChain?](#why-julep-vs-langchain) + - [Different Use Cases](#different-use-cases) + - [Different Form Factor](#different-form-factor) + - [In Summary](#in-summary) +- [Installation](#installation) +- [Python Quick Start 🐍](#python-quick-start-) + - [Step 1: Create an Agent](#step-1-create-an-agent) + - [Step 2: Create a Task that generates a story and comic strip](#step-2-create-a-task-that-generates-a-story-and-comic-strip) + - [Step 3: Execute the Task](#step-3-execute-the-task) + - [Step 4: Chat with the Agent](#step-4-chat-with-the-agent) +- [Node.js Quick Start 🟩](#nodejs-quick-start-) + - [Step 1: Create an Agent](#step-1-create-an-agent-1) + - [Step 2: Create a Task that generates a story and comic strip](#step-2-create-a-task-that-generates-a-story-and-comic-strip-1) + - [Step 3: Execute the Task](#step-3-execute-the-task-1) + - [Step 4: Chat with the Agent](#step-4-chat-with-the-agent-1) +- [Components](#components) + - [Mental Model](#mental-model) +- [Concepts](#concepts) +- [Understanding Tasks](#understanding-tasks) + - [Types of Workflow Steps](#types-of-workflow-steps) + - [Common Steps](#common-steps) + - [Key-Value Steps](#key-value-steps) + - [Iteration Steps](#iteration-steps) + - [Conditional Steps](#conditional-steps) + - [Other Control Flow](#other-control-flow) +- [Advanced Features](#advanced-features) + - [Adding Tools to Agents](#adding-tools-to-agents) + - [Managing Sessions and Users](#managing-sessions-and-users) + - [Document Integration and Search](#document-integration-and-search) +- [Integrations](#integrations) + - [Brave Search](#brave-search) + - [BrowserBase](#browserbase) + - [Email](#email) + - [Spider](#spider) + - [Weather](#weather) + - [Wikipedia](#wikipedia) +- [SDK Reference](#sdk-reference) +- [API Reference](#api-reference) -# Start an interactive chat session with the agent -session = client.sessions.create( - agent_id=agent.id, - context_overflow="adaptive", # 🧠 Julep will dynamically compute the context window if needed -) +
+ -# 🔄 Chat loop -while (user_input := input("You: ")) != "exit": - response = client.sessions.chat( - session_id=session.id, - message=user_input, - ) +## Introduction - print("Agent: ", response.choices[0].message.content) +Julep is a platform for creating AI agents that remember past interactions and can perform complex tasks. It offers long-term memory and manages multi-step processes. +Julep enables the creation of multi-step tasks incorporating decision-making, loops, parallel processing, and integration with numerous external tools and APIs. -################# -## 📋 Task 📋 ## -################# +While many AI applications are limited to simple, linear chains of prompts and API calls with minimal branching, Julep is built to handle more complex scenarios. -# Create a recurring research task for the agent -task = client.tasks.create( - agent_id=agent.id, - name="Research Task", - description="Research the given topic every 24 hours.", - # - # 🛠️ Task specific tools - tools=[ - { - "name": "send_email", - "description": "Send an email to the user with the results.", - "api_call": { - "method": "post", - "url": "https://api.sendgrid.com/v3/mail/send", - "headers": {"Authorization": "Bearer YOUR_SENDGRID_API_KEY"}, - }, - } - ], - # - # 🔢 Task main steps - main=[ - # - # Step 1: Research the topic - { - # `_` (underscore) variable refers to the previous step's output - # Here, it points to the topic input from the user - "prompt": "Look up topic '{{_.topic}}' and summarize the results.", - "tools": [{"ref": {"name": "web_search"}}], # 🔍 Use the web search tool from the agent - "unwrap": True, - }, - # - # Step 2: Send email with research results - { - "tool": "send_email", - "arguments": { - "subject": "Research Results", - "body": "'Here are the research results for today: ' + _.content", - "to": "inputs[0].email", # Reference the email from the user's input - }, - }, - # - # Step 3: Wait for 24 hours before repeating - {"sleep": "24 * 60 * 60"}, - ], -) +It supports: -# 🚀 Start the recurring task -client.executions.create(task_id=task.id, input={"topic": "Python"}) +- Intricate, multi-step processes +- Dynamic decision-making +- Parallel execution -# 🔁 This will run the task every 24 hours, -# research for the topic "Python", and -# send the results to the user's email -
-
+> [!TIP] +> Imagine you want to build an AI agent that can do more than just answer simple questions—it needs to handle complex tasks, remember past interactions, and maybe even use other tools or APIs. That's where Julep comes in. + +## Quick Example + +Imagine a Research AI agent that can do the following: + +1. Take a topic, +2. Come up with 100 search queries for that topic, +3. Perform those web searches in parallel, +4. Summarize the results, +5. Send the summary to Discord + +In Julep, this would be a single task under 80 lines of code and run fully managed all on its own. All of the steps are executed on Julep's own servers and you don't need to lift a finger. Here's a working example: + +```yaml +name: Research Agent + +# Optional: Define the input schema for the task +input_schema: + type: object + properties: + topic: + type: string + description: The main topic to research + +# Define the tools that the agent can use +tools: + - name: web_search + type: integration + integration: + provider: brave + setup: + api_key: "YOUR_BRAVE_API_KEY" + + - name: discord_webhook + type: api_call + api_call: + url: "YOUR_DISCORD_WEBHOOK_URL" + method: POST + headers: + Content-Type: application/json + +# Special variables: +# - inputs: for accessing the input to the task +# - outputs: for accessing the output of previous steps +# - _: for accessing the output of the previous step + +# Define the main workflow +main: + - prompt: + - role: system + content: >- + You are a research assistant. + Generate 100 diverse search queries related to the topic: + {{inputs[0].topic}} + + Write one query per line. + unwrap: true + + # Evaluate the search queries using a simple python expression + - evaluate: + search_queries: "_.split('\n')" + + # Run the web search in parallel for each query + - over: "_.search_queries" + map: + tool: web_search + arguments: + query: "_" + parallelism: 100 + + # Collect the results from the web search + - evaluate: + results: "'\n'.join([item.result for item in _])" + + # Summarize the results + - prompt: + - role: system + content: > + You are a research summarizer. Create a comprehensive summary of the following research results on the topic {{inputs[0].topic}}. + The summary should be well-structured, informative, and highlight key findings and insights: + {{_.results}} + unwrap: true + + # Send the summary to Discord + - tool: discord_webhook + arguments: + content: > + **Research Summary for {{inputs[0].topic}}** + + {{_}} +``` +> [!TIP] +> Julep is really useful when you want to build AI agents that can maintain context and state over long-term interactions. It's great for designing complex, multi-step workflows and integrating various tools and APIs directly into your agent's processes. +> +> In this example, Julep will automatically manage parallel executions, retry failed steps, resend API requests, and keep the tasks running reliably until completion. -## Features +## Key Features -Julep simplifies the process of building persistent AI agents with customizable workflows. Key features include: +1. 🧠 **Persistent AI Agents**: Remember context and information over long-term interactions. +2. 💾 **Stateful Sessions**: Keep track of past interactions for personalized responses. +3. 🔄 **Multi-Step Tasks**: Build complex, multi-step processes with loops and decision-making. +4. ⏳ **Task Management**: Handle long-running tasks that can run indefinitely. +5. 🛠️ **Built-in Tools**: Use built-in tools and external APIs in your tasks. +6. 🔧 **Self-Healing**: Julep will automatically retry failed steps, resend messages, and generally keep your tasks running smoothly. +7. 📚 **RAG**: Use Julep's document store to build a system for retrieving and using your own data. -- **Persistent AI Agents**: Create and manage AI agents that maintain context across interactions. -- **Customizable Workflows**: Design complex, multi-step AI workflows using Tasks. -- **Tool Integration**: Seamlessly integrate various tools and APIs into your AI workflows. -- **Document Management**: Efficiently manage and search through documents for your agents. -- **Session Management**: Handle persistent sessions for continuous interactions. -- **Flexible Execution**: Support for parallel processing, conditional logic, and error handling in workflows. +Julep is ideal for applications that require AI use cases beyond simple prompt-response models. -## Installation +## Why Julep vs. LangChain? -To get started with Julep, install it using [npm](https://www.npmjs.com/package/@julep/sdk) or [pip](https://pypi.org/project/julep/): +### Different Use Cases -```bash -npm install @julep/sdk -``` +Think of LangChain and Julep as tools with different focuses within the AI development stack. -or +LangChain is great for creating sequences of prompts and managing interactions with AI models. It has a large ecosystem with lots of pre-built integrations, which makes it convenient if you want to get something up and running quickly. LangChain fits well with simple use cases that involve a linear chain of prompts and API calls. -```bash -pip install julep -``` +Julep, on the other hand, is more about building persistent AI agents that can remember things over long-term interactions. It shines when you need complex tasks that involve multiple steps, decision-making, and integration with various tools or APIs directly within the agent's process. It's designed from the ground up to manage persistent sessions and complex tasks. -> [!TIP] -> ~~Get your API key [here](https://app.julep.ai/api-keys).~~ -> -> While we are in beta, you can reach out on [Discord](https://discord.com/invite/JTSBGRZrzj) to get your API key. +Use Julep if you imagine building a complex AI assistant that needs to: -## Quick Start Guide +- Keep track of user interactions over days or weeks. +- Perform scheduled tasks, like sending daily summaries or monitoring data sources. +- Make decisions based on prior interactions or stored data. +- Interact with multiple external services as part of its task. -### Step 1: Import Julep +Then Julep provides the infrastructure to support all that without you having to build it from scratch. -First, import the Julep SDK into your project: +### Different Form Factor -```javascript -const Julep = require('@julep/sdk'); -``` +Julep is a **platform** that includes a language for describing tasks, a server for running those tasks, and an SDK for interacting with the platform. To build something with Julep, you write a description of the task in `YAML`, and then run the task in the cloud. -or +Julep is built for heavy-lifting, multi-step, and long-running tasks and there's no limit to how complex the task can be. -```python -from julep import AsyncJulep -``` +LangChain is a **library** that includes a few tools and a framework for building linear chains of prompts and tools. To build something with LangChain, you typically write Python code that configures and runs the model chains you want to use. -### Step 2: Initialize the Agent +LangChain might be sufficient and quicker to implement for simple use cases that involve a linear chain of prompts and API calls. -Create a new agent with basic settings: +### In Summary -```javascript -const julep = new Julep({ apiKey: 'your-api-key' }); +Use LangChain when you need to manage AI model interactions and prompt sequences in a stateless or short-term context. -const agent = await julep.agents.create({ - name: 'ResearchAssistant', - model: 'gpt-4-turbo', - about: "You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", -}); -``` +Choose Julep when you need a robust framework for stateful agents with advanced task capabilities, persistent sessions, and complex task management. -or +## Installation -```python -client = AsyncJulep(api_key="your_api_key") +To get started with Julep, install it using [npm](https://www.npmjs.com/package/@julep/sdk) or [pip](https://pypi.org/project/julep/): -agent = await client.agents.create( - name="Storytelling Agent", - model="gpt-4-turbo", - about="You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", -) +```bash +npm install @julep/sdk ``` -### Step 3: Chat with the Agent - -Start an interactive chat session with the agent: - -```javascript -const session = await julep.sessions.create({ - agentId: agent.id, -}); - -// Send messages to the agent -const response = await julep.sessions.chat({ - sessionId: session.id, - message: 'Hello, can you tell me a story?', -}); +or -console.log(response); +```bash +pip install julep ``` -or +> [!NOTE] +> Get your API key [here](https://dashboard-dev.julep.ai). +> +> While we are in beta, you can also reach out on [Discord](https://discord.com/invite/JTSBGRZrzj) to get rate limits lifted on your API key. -```python -session = await client.sessions.create(agent_id=agent.id) +> [!TIP] +> 💻 Are you a _show me the code!™_ kind of person? We have created a ton of cookbooks for you to get started with. **Check out the [cookbooks](https://github.com/julep-ai/julep/tree/dev/cookbooks)** to browse through examples. +> +> 💡 There's also lots of ideas that you can build on top of Julep. **Check out the [list of ideas](https://github.com/julep-ai/julep/tree/dev/cookbooks/IDEAS.md)** to get some inspiration. -# Send messages to the agent -response = await client.sessions.chat( - session_id=session.id, - message="Hello, can you tell me a story?", -) +## Python Quick Start 🐍 -print(response) -``` +### Step 1: Create an Agent +```python +import yaml +from julep import Julep # or AsyncJulep -### Step 4: Create a multi-step Task +client = Julep(api_key="your_julep_api_key", environment=“dev”) -Let's define a multi-step task to create a story and generate a paneled comic strip based on an input idea: +agent = client.agents.create( + name="Storytelling Agent", + model="gpt-4o", + about="You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", +) -```python # 🛠️ Add an image generation tool (DALL·E) to the agent -await client.agents.tools.create( +client.agents.tools.create( agent_id=agent.id, name="image_generator", description="Use this tool to generate images based on descriptions.", @@ -269,182 +324,328 @@ await client.agents.tools.create( "provider": "dalle", "method": "generate_image", "setup": { - "api_key": "your_dalle_api_key", + "api_key": "your_openai_api_key", }, }, ) +``` + +### Step 2: Create a Task that generates a story and comic strip +Let's define a multi-step task to create a story and generate a paneled comic strip based on an input idea: + +```python # 📋 Task # Create a task that takes an idea and creates a story and a 4-panel comic strip -task = await client.tasks.create( +task_yaml = """ +name: Story and Comic Creator +description: Create a story based on an idea and generate a 4-panel comic strip illustrating the story. + +main: + # Step 1: Generate a story and outline into 4 panels + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. + Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story. + unwrap: true + + # Step 2: Extract the panel descriptions and story + - evaluate: + story: _.split('1. ')[0].strip() + panels: re.findall(r'\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)', _) + + # Step 3: Generate images for each panel using the image generator tool + - foreach: + in: _.panels + do: + tool: image_generator + arguments: + description: _ + + # Step 4: Generate a catchy title for the story + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the story below, generate a catchy title. + + Story: {{outputs[1].story}} + unwrap: true + + # Step 5: Return the story, the generated images, and the title + - return: + title: outputs[3] + story: outputs[1].story + comic_panels: "[output.image.url for output in outputs[2]]" +""" + +task = client.tasks.create( agent_id=agent.id, - name="Story and Comic Creator", - description="Create a story based on an idea and generate a 4-panel comic strip illustrating the story.", - main=[ - # Step 1: Generate a story and outline into 4 panels - { - "prompt": [ - { - "role": "system", - "content": "You are {{agent.name}}. {{agent.about}}" - }, - { - "role": "user", - "content": ( - "Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. " - "Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story." - ), - }, - ], - "unwrap": True, - }, - # Step 2: Extract the panel descriptions and story - { - "evaluate": { - "story": "_.split('1. ')[0].strip()", - "panels": "re.findall(r'\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)', _)", - } - }, - # Step 3: Generate images for each panel using the image generator tool - { - "foreach": { - "in": "_.panels", - "do": { - "tool": "image_generator", - "arguments": { - "description": "_", - }, - }, - }, - }, - # Step 4: Generate a catchy title for the story - { - "prompt": [ - { - "role": "system", - "content": "You are {{agent.name}}. {{agent.about}}" - }, - { - "role": "user", - "content": "Based on the story below, generate a catchy title.\n\nStory: {{outputs[1].story}}", - }, - ], - "unwrap": True, - }, - # Step 5: Return the story, the generated images, and the title - { - "return": { - "title": "outputs[3]", - "story": "outputs[1].story", - "comic_panels": "[output.image.url for output in outputs[2]]", - } - }, - ], + **yaml.safe_load(task_yaml) ) ``` -> [!TIP] -> node.js version of this is similar. - -### Step 5: Execute the Task +### Step 3: Execute the Task ```python # 🚀 Execute the task with an input idea -execution = await client.executions.create( +execution = client.executions.create( task_id=task.id, input={"idea": "A cat who learns to fly"} ) # 🎉 Watch as the story and comic panels are generated -await client.executions.stream(execution_id=execution.id) +for transition in client.executions.transitions.stream(execution_id=execution.id): + print(transition) + +# 📦 Once the execution is finished, retrieve the results +result = client.executions.get(execution_id=execution.id) ``` -This example demonstrates how to create an agent with a custom tool, define a complex task with multiple steps, and execute it to generate a creative output. +### Step 4: Chat with the Agent + +Start an interactive chat session with the agent: - +```python +session = client.sessions.create(agent_id=agent.id) + +# 💬 Send messages to the agent +while (message := input("Enter a message: ")) != "quit": + response = client.sessions.chat( + session_id=session.id, + message=message, + ) + + print(response) +``` > [!TIP] -> You can find another node.js example [here](example.ts) or python example [here](example.py). +> You can find the full python example [here](example.py). -## Concepts +## Node.js Quick Start 🟩 -Julep is built on several key technical components that work together to create powerful AI workflows: +### Step 1: Create an Agent -### Agents -AI-powered entities backed by large language models (LLMs) that execute tasks and interact with users. Agents are the core functional units of Julep. +```javascript +import { Julep } from "@julep/sdk"; +import yaml from "js-yaml"; + +const client = new Julep({ apiKey: "your_julep_api_key",environment:“dev” }); + +async function createAgent() { + const agent = await client.agents.create({ + name: "Storytelling Agent", + model: "gpt-4", + about: + "You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", + }); + + // 🛠️ Add an image generation tool (DALL·E) to the agent + await client.agents.tools.create(agent.id, { + name: "image_generator", + description: "Use this tool to generate images based on descriptions.", + integration: { + provider: "dalle", + method: "generate_image", + setup: { + api_key: "your_openai_api_key", + }, + }, + }); -```mermaid -graph TD - Agent[Agent] --> LLM[Large Language Model] - Agent --> Tasks[Tasks] - Agent --> Users[Users] - Tasks --> Tools[Tools] + return agent; +} ``` -### Users -Entities that interact with agents. Users can be associated with sessions and have their own metadata, allowing for personalized interactions. +### Step 2: Create a Task that generates a story and comic strip -```mermaid -graph LR - User[User] --> Sessions[Sessions] - Sessions --> Agents[Agents] - Sessions --> Metadata[Metadata] +```javascript +const taskYaml = ` +name: Story and Comic Creator +description: Create a story based on an idea and generate a 4-panel comic strip illustrating the story. + +main: + # Step 1: Generate a story and outline into 4 panels + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. + Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story. + unwrap: true + + # Step 2: Extract the panel descriptions and story + - evaluate: + story: _.split('1. ')[0].trim() + panels: _.match(/\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)/g) + + # Step 3: Generate images for each panel using the image generator tool + - foreach: + in: _.panels + do: + tool: image_generator + arguments: + description: _ + + # Step 4: Generate a catchy title for the story + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the story below, generate a catchy title. + + Story: {{outputs[1].story}} + unwrap: true + + # Step 5: Return the story, the generated images, and the title + - return: + title: outputs[3] + story: outputs[1].story + comic_panels: outputs[2].map(output => output.image.url) +`; + +async function createTask(agent) { + const task = await client.tasks.create(agent.id, yaml.load(taskYaml)); + return task; +} ``` -### Sessions -Stateful interactions between agents and users. Sessions maintain context across multiple exchanges and can be configured for different behaviors, including context management and overflow handling. +### Step 3: Execute the Task -```mermaid -graph LR - Sessions[Sessions] --> Agents[Agents] - Sessions --> Users[Users] - Sessions --> ContextManagement[Context Management] - Sessions --> OverflowHandling[Overflow Handling] +```javascript +async function executeTask(task) { + const execution = await client.executions.create(task.id, { + input: { idea: "A cat who learns to fly" }, + }); + + // 🎉 Watch as the story and comic panels are generated + for await (const transition of client.executions.transitions.stream( + execution.id + )) { + console.log(transition); + } + + // 📦 Once the execution is finished, retrieve the results + const result = await client.executions.get(execution.id); + return result; +} ``` -### Tasks -Multi-step, programmatic workflows that agents can execute. Tasks define complex operations and can include various types of steps, such as prompts, tool calls, and conditional logic. +### Step 4: Chat with the Agent -```mermaid -graph TD - Tasks[Tasks] --> Steps[Workflow Steps] - Steps --> Prompt[Prompt] - Steps --> ToolCalls[Tool Calls] - Steps --> ConditionalLogic[Conditional Logic] +```javascript +async function chatWithAgent(agent) { + const session = await client.sessions.create({ agent_id: agent.id }); + + // 💬 Send messages to the agent + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const chat = async () => { + rl.question("Enter a message (or 'quit' to exit): ", async (message) => { + if (message.toLowerCase() === "quit") { + rl.close(); + return; + } + + const response = await client.sessions.chat(session.id, { message }); + console.log(response); + chat(); + }); + }; + + chat(); +} + +// Run the example +async function runExample() { + const agent = await createAgent(); + const task = await createTask(agent); + const result = await executeTask(task); + console.log("Task Result:", result); + await chatWithAgent(agent); +} + +runExample().catch(console.error); ``` -### Tools -Integrations that extend an agent's capabilities. Tools can be user-defined functions, system tools, or third-party API integrations. They allow agents to perform actions beyond text generation. +> [!TIP] +> You can find the full Node.js example [here](example.js). -```mermaid -graph LR - Tools[Tools] --> UserDefinedFunctions[User-Defined Functions] - Tools --> SystemTools[System Tools] - Tools --> ThirdPartyAPIs[Third-Party APIs] -``` +## Components -### Documents -Text or data objects that can be associated with agents or users. Documents are vectorized and stored in a vector database, enabling semantic search and retrieval during agent interactions. +Julep is made up of the following components: -```mermaid -graph LR - Documents[Documents] --> VectorDatabase[Vector Database] - Documents --> SemanticSearch[Semantic Search] - Documents --> AgentsOrUsers[Agents or Users] -``` +- **Julep Platform**: The Julep platform is a cloud service that runs your workflows. It includes a language for describing workflows, a server for running those workflows, and an SDK for interacting with the platform. +- **Julep SDKs**: Julep SDKs are a set of libraries for building workflows. There are SDKs for Python and JavaScript, with more on the way. +- **Julep API**: The Julep API is a RESTful API that you can use to interact with the Julep platform. + +### Mental Model + +
+ +
+ +Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's how to visualize it: + +1. **Your Application Code:** + + - You use the Julep SDK in your application to define agents, tasks, and workflows. + - The SDK provides functions and classes that make it easy to set up and manage these components. + +2. **Julep Backend Service:** + + - The SDK communicates with the Julep backend over the network. + - The backend handles execution of tasks, maintains session state, stores documents, and orchestrates workflows. -### Executions -Instances of tasks that have been initiated with specific inputs. Executions have their own lifecycle and state machine, allowing for monitoring, management, and resumption of long-running processes. +3. **Integration with Tools and APIs:** + - Within your workflows, you can integrate external tools and services. + - The backend facilitates these integrations, so your agents can, for example, perform web searches, access databases, or call third-party APIs. + +In simpler terms: + +- Julep is a platform for building stateful AI agents. +- You use the SDK (like a toolkit) in your code to define what your agents do. +- The backend service (which you can think of as the engine) runs these definitions, manages state, and handles complexity. + +## Concepts + +Julep is built on several key technical components that work together to create powerful AI workflows: ```mermaid -graph LR - Executions[Executions] --> Tasks[Tasks] - Executions --> Lifecycle[Lifecycle] - Executions --> Monitoring[Monitoring] - Executions --> Management[Management] - Executions --> Resumption[Resumption] +graph TD + User[User] ==> Session[Session] + Session --> Agent[Agent] + Agent --> Tasks[Tasks] + Agent --> LLM[Large Language Model] + Tasks --> Tools[Tools] + Agent --> Documents[Documents] + Documents --> VectorDB[Vector Database] + Tasks --> Executions[Executions] + + classDef client fill:#9ff,stroke:#333,stroke-width:1px; + class User client; + + classDef core fill:#f9f,stroke:#333,stroke-width:2px; + class Agent,Tasks,Session core; ``` +- **Agents**: AI-powered entities backed by large language models (LLMs) that execute tasks and interact with users. +- **Users**: Entities that interact with agents through sessions. +- **Sessions**: Stateful interactions between agents and users, maintaining context across multiple exchanges. +- **Tasks**: Multi-step, programmatic workflows that agents can execute, including various types of steps like prompts, tool calls, and conditional logic. +- **Tools**: Integrations that extend an agent's capabilities, including user-defined functions, system tools, or third-party API integrations. +- **Documents**: Text or data objects associated with agents or users, vectorized and stored for semantic search and retrieval. +- **Executions**: Instances of tasks that have been initiated with specific inputs, with their own lifecycle and state machine. + For a more detailed explanation of these concepts and their interactions, please refer to our [Concepts Documentation](https://github.com/julep-ai/julep/blob/dev/docs/julep-concepts.md). ## Understanding Tasks @@ -457,55 +658,148 @@ Tasks are the core of Julep's workflow system. They allow you to define complex, ### Types of Workflow Steps -Tasks in Julep can include various types of steps: +Tasks in Julep can include various types of steps, allowing you to create complex and powerful workflows. Here's an overview of the available step types, organized by category: + +#### Common Steps 1. **Prompt**: Send a message to the AI model and receive a response. - ```python - {"prompt": "Analyze the following data: {{data}}"} + + ```yaml + - prompt: "Analyze the following data: {{data}}" ``` 2. **Tool Call**: Execute an integrated tool or API. - ```python - {"tool": "web_search", "arguments": {"query": "Latest AI developments"}} + + ```yaml + - tool: web_search + arguments: + query: "Latest AI developments" ``` 3. **Evaluate**: Perform calculations or manipulate data. - ```python - {"evaluate": {"average_score": "sum(scores) / len(scores)"}} + + ```yaml + - evaluate: + average_score: "sum(scores) / len(scores)" + ``` + +4. **Wait for Input**: Pause workflow until input is received. + + ```yaml + - wait_for_input: + info: + message: "Please provide additional information." + ``` + +5. **Log**: Log a specified value or message. + ```yaml + - log: "Processing completed for item {{item_id}}" + ``` + +#### Key-Value Steps + +6. **Get**: Retrieve a value from a key-value store. + + ```yaml + - get: "user_preference" + ``` + +7. **Set**: Assign a value to a key in a key-value store. + ```yaml + - set: + user_preference: "dark_mode" ``` -4. **Conditional Logic**: Execute steps based on conditions. - ```python - {"if": "score > 0.8", "then": [...], "else": [...]} +#### Iteration Steps + +8. **Foreach**: Iterate over a collection and perform steps for each item. + + ```yaml + - foreach: + in: "data_list" + do: + - log: "Processing item {{_}}" ``` -5. **Loops**: Iterate over data or repeat steps. - ```python - {"foreach": {"in": "data_list", "do": [...]}} +9. **Map-Reduce**: Map over a collection and reduce the results. + + ```yaml + - map_reduce: + over: "numbers" + map: + - evaluate: + squared: "_ ** 2" + reduce: "sum(results)" ``` -| Step Name | Description | Input | -|--------------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------| -| **Prompt** | Send a message to the AI model and receive a response. | Prompt text or template | -| **Tool Call** | Execute an integrated tool or API. | Tool name and arguments | -| **Evaluate** | Perform calculations or manipulate data. | Expressions or variables to evaluate | -| **Wait for Input** | Pause workflow until input is received. | Any required user or system input | -| **Log** | Log a specified value or message. | Message or value to log | -| **Embed** | Embed text into a specific format or system. | Text or content to embed | -| **Search** | Perform a document search based on a query. | Search query | -| **Get** | Retrieve a value from a key-value store. | Key identifier | -| **Set** | Assign a value to a key in a key-value store. | Key and value to assign | -| **Parallel** | Run multiple steps in parallel. | List of steps to execute simultaneously | -| **Foreach** | Iterate over a collection and perform steps for each item. | Collection or list to iterate over | -| **MapReduce** | Map over a collection and reduce the results based on an expression. | Collection to map and reduce expressions | -| **If Else** | Conditional execution of steps based on a condition. | Condition to evaluate | -| **Switch** | Execute steps based on multiple conditions, similar to a switch-case statement. | Multiple conditions and corresponding steps | -| **Yield** | Run a subworkflow and await its completion. | Subworkflow identifier and input data | -| **Error** | Handle errors by specifying an error message. | Error message or handling instructions | -| **Sleep** | Pause the workflow for a specified duration. | Duration (seconds, minutes, etc.) | -| **Return** | Return a value from the workflow. | Value to return | - -For detailed information on each step type and advanced usage, please refer to our [Task Documentation](https://docs.julep.ai/tasks). +10. **Parallel**: Run multiple steps in parallel. + ```yaml + - parallel: + - tool: web_search + arguments: + query: "AI news" + - tool: weather_check + arguments: + location: "New York" + ``` + +#### Conditional Steps + +11. **If-Else**: Conditional execution of steps. + + ```yaml + - if: "score > 0.8" + then: + - log: "High score achieved" + else: + - log: "Score needs improvement" + ``` + +12. **Switch**: Execute steps based on multiple conditions. + ```yaml + - switch: + - case: "category == 'A'" + then: + - log: "Category A processing" + - case: "category == 'B'" + then: + - log: "Category B processing" + - case: "_" # Default case + then: + - log: "Unknown category" + ``` + +#### Other Control Flow + +13. **Sleep**: Pause the workflow for a specified duration. + + ```yaml + - sleep: + seconds: 30 + ``` + +14. **Return**: Return a value from the workflow. + + ```yaml + - return: + result: "Task completed successfully" + ``` + +15. **Yield**: Run a subworkflow and await its completion. + + ```yaml + - yield: + workflow: "data_processing_subflow" + arguments: + input_data: "{{raw_data}}" + ``` + +16. **Error**: Handle errors by specifying an error message. + ```yaml + - error: "Invalid input provided" + ``` + +Each step type serves a specific purpose in building sophisticated AI workflows. This categorization helps in understanding the various control flows and operations available in Julep tasks. ## Advanced Features @@ -521,9 +815,9 @@ client.agents.tools.create( name="web_search", description="Search the web for information.", integration={ - "provider": "google", + "provider": "brave", "method": "search", - "setup": {"api_key": "your_google_api_key"}, + "setup": {"api_key": "your_brave_api_key"}, }, ) ``` @@ -535,14 +829,19 @@ Julep provides robust session management for persistent interactions: ```python session = client.sessions.create( agent_id=agent.id, - user_id="user123", + user_id=user.id, context_overflow="adaptive" ) # Continue conversation in the same session response = client.sessions.chat( session_id=session.id, - message="Follow up on our previous conversation." + messages=[ + { + "role": "user", + "content": "Follow up on the previous conversation." + } + ] ) ``` @@ -552,59 +851,122 @@ Easily manage and search through documents for your agents: ```python # Upload a document -document = client.documents.create( - file="path/to/document.pdf", +document = client.agents.docs.create( + title="AI advancements", + content="AI is changing the world...", metadata={"category": "research_paper"} ) # Search documents -results = client.documents.search( - query="AI advancements", - filter={"category": "research_paper"} +results = client.agents.docs.search( + text="AI advancements", + metadata_filter={"category": "research_paper"} ) ``` For more advanced features and detailed usage, please refer to our [Advanced Features Documentation](https://docs.julep.ai/advanced-features). -## SDK Reference +## Integrations -- [Node.js SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) -- [Python SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) +Julep supports various integrations that extend the capabilities of your AI agents. Here's a list of available integrations and their supported arguments: -## API Reference +### Brave Search -Explore our comprehensive API documentation to learn more about agents, tasks, and executions: +```yaml +setup: + api_key: string # The API key for Brave Search -- [Agents API](https://api.julep.ai/api/docs#tag/agents) -- [Tasks API](https://api.julep.ai/api/docs#tag/tasks) -- [Executions API](https://api.julep.ai/api/docs#tag/executions) +arguments: + query: string # The search query for searching with Brave -## Examples and Tutorials +output: + result: string # The result of the Brave Search +``` + +### BrowserBase + +```yaml +setup: + api_key: string # The API key for BrowserBase + project_id: string # The project ID for BrowserBase + session_id: string # (Optional) The session ID for BrowserBase + +arguments: + urls: list[string] # The URLs for loading with BrowserBase + +output: + documents: list # The documents loaded from the URLs +``` + +### Email + +```yaml +setup: + host: string # The host of the email server + port: integer # The port of the email server + user: string # The username of the email server + password: string # The password of the email server + +arguments: + to: string # The email address to send the email to + from: string # The email address to send the email from + subject: string # The subject of the email + body: string # The body of the email + +output: + success: boolean # Whether the email was sent successfully +``` + +### Spider -Discover example projects and tutorials to help you get started and build upon provided examples: +```yaml +setup: + spider_api_key: string # The API key for Spider -- [Example Projects](https://github.com/julep-ai/julep/tree/main/examples) -- [Tutorials](https://docs.julep.ai/tutorials) +arguments: + url: string # The URL for which to fetch data + mode: string # The type of crawlers (default: "scrape") + params: dict # (Optional) The parameters for the Spider API -## Contributing +output: + documents: list # The documents returned from the spider +``` + +### Weather + +```yaml +setup: + openweathermap_api_key: string # The API key for OpenWeatherMap + +arguments: + location: string # The location for which to fetch weather data + +output: + result: string # The weather data for the specified location +``` -We welcome contributions to the project! Learn how to contribute and our code of conduct: +### Wikipedia -- [Contributing Guidelines](https://github.com/julep-ai/julep/blob/main/CONTRIBUTING.md) -- [Code of Conduct](https://github.com/julep-ai/julep/blob/main/CODE_OF_CONDUCT.md) +```yaml +arguments: + query: string # The search query string + load_max_docs: integer # Maximum number of documents to load (default: 2) -## Support and Community +output: + documents: list # The documents returned from the Wikipedia search +``` -Join our community to get help, ask questions, and share your ideas: +These integrations can be used within your tasks to extend the capabilities of your AI agents. For more detailed information on how to use these integrations in your workflows, please refer to our [Integrations Documentation](https://docs.julep.ai/integrations). -- [Discord](https://discord.com/invite/JTSBGRZrzj) -- [GitHub Discussions](https://github.com/julep-ai/julep/discussions) -- [Twitter](https://twitter.com/julep_ai) +## SDK Reference -## License +- [Node.js SDK](https://github.com/julep-ai/node-sdk/blob/main/api.md) +- [Python SDK](https://github.com/julep-ai/python-sdk/blob/main/api.md) -This project is licensed under the [Apache License 2.0](https://github.com/julep-ai/julep/blob/main/LICENSE). +## API Reference -## Acknowledgements +Explore our comprehensive API documentation to learn more about agents, tasks, and executions: -We would like to express our gratitude to all contributors and the open-source community for their valuable resources and contributions. +- [Agents API](https://dev.julep.ai/api/docs#tag/agents) +- [Tasks API](https://dev.julep.ai/api/docs#tag/tasks) +- [Executions API](https://dev.julep.ai/api/docs#tag/executions) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 85a3f43b3..61df6b682 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -52,4 +52,3 @@ * [⭐ Github](https://github.com/julep-ai/julep) * [🐍 PyPI package](https://pypi.org/project/julep/) * [📦 npm package](https://www.npmjs.com/package/@julep/sdk) -* [📫 Postman Collection](https://god.gw.postman.com/run-collection/33213061-a0a1e3a9-9681-44ae-a5c2-703912b32336?action=collection%2Ffork\&source=rip_markdown\&collection-url=entityId%3D33213061-a0a1e3a9-9681-44ae-a5c2-703912b32336%26entityType%3Dcollection%26workspaceId%3D183380b4-f2ac-44ef-b018-1f65dfc8256b) diff --git a/docs/api-reference/README.md b/docs/api-reference/README.md index ed15fbb56..a3c3c2c96 100644 --- a/docs/api-reference/README.md +++ b/docs/api-reference/README.md @@ -1,3 +1,4 @@ + # Agents API [![Run In Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/33213061-a0a1e3a9-9681-44ae-a5c2-703912b32336?action=collection%2Ffork\&source=rip\_markdown\&collection-url=entityId%3D33213061-a0a1e3a9-9681-44ae-a5c2-703912b32336%26entityType%3Dcollection%26workspaceId%3D183380b4-f2ac-44ef-b018-1f65dfc8256b) diff --git a/docs/api-reference/agents-api-5.md b/docs/api-reference/agents-api-5.md index 86c29ae67..a1bb28348 100644 --- a/docs/api-reference/agents-api-5.md +++ b/docs/api-reference/agents-api-5.md @@ -1,6 +1,13 @@ # Tasks +***** +> ### This docs site is currently under construction although this github README below should suffice for now. + +![](https://i.giphy.com/vR1dPIYzQmkRzLZk2w.webp) +***** + + {% hint style="info" %} Coming Soon diff --git a/docs/api-reference/agents-api-6.md b/docs/api-reference/agents-api-6.md index f5f5bc7ed..44581cd22 100644 --- a/docs/api-reference/agents-api-6.md +++ b/docs/api-reference/agents-api-6.md @@ -1,5 +1,12 @@ # Task Runs +***** +> ### This docs site is currently under construction although this github README below should suffice for now. + +![](https://i.giphy.com/vR1dPIYzQmkRzLZk2w.webp) +***** + + {% hint style="info" %} **Coming soon.** {% endhint %} diff --git a/docs/api-reference/agents-api.md b/docs/api-reference/agents-api.md index 783a75e74..001d1a349 100644 --- a/docs/api-reference/agents-api.md +++ b/docs/api-reference/agents-api.md @@ -1,7 +1,6 @@ --- description: API for creating and modifying Agents --- - # Agents ## List agents diff --git a/docs/concepts/sessions/adaptive-context.md b/docs/concepts/sessions/adaptive-context.md index 8bb7cf801..e870ffbe9 100644 --- a/docs/concepts/sessions/adaptive-context.md +++ b/docs/concepts/sessions/adaptive-context.md @@ -65,7 +65,7 @@ Enabling Adaptive Context is straightforward and can be done when creating a new from julep import Client # Initialize the Julep client -client = Client(api_key="your_api_key") +client = Client(api_key="your_api_key", environment=“dev”) # Create a new session with Adaptive Context enabled session = client.sessions.create( @@ -84,7 +84,7 @@ session = client.sessions.create( import { Client } from "@julep/sdk"; // Initialize the Julep client -const client = new Client({ apiKey: "your-api-key" }); +const client = new Client({ apiKey: "your-api-key", environment:“dev” }); // Create a new session with Adaptive Context enabled const session = await client.sessions.create({ diff --git a/docs/explanation/default_system_template.md b/docs/explanation/default_system_template.md index 3d0f5d272..53bdeabeb 100644 --- a/docs/explanation/default_system_template.md +++ b/docs/explanation/default_system_template.md @@ -8,7 +8,7 @@ You are {{agent.name}}.{{" "}} {%- endif -%} {%- if agent.about -%} -About you: {{agent.name}}.{{" "}} +About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} diff --git a/docs/explanation/task_workflows.md b/docs/explanation/task_workflows.md index 7c77ff686..29606ea5a 100644 --- a/docs/explanation/task_workflows.md +++ b/docs/explanation/task_workflows.md @@ -1,5 +1,6 @@ # Task Workflows in Julep + Tasks in Julep are powerful, Github Actions-style workflows that define long-running, multi-step actions. They allow for complex operations by defining steps and have access to all Julep integrations. ## Task Structure diff --git a/docs/how-to-guides/customizing_tasks.md b/docs/how-to-guides/customizing_tasks.md index 29a39dd19..8c3885e1a 100644 --- a/docs/how-to-guides/customizing_tasks.md +++ b/docs/how-to-guides/customizing_tasks.md @@ -7,7 +7,7 @@ This guide covers how to define and customize tasks for agents in Julep. Here's an example of creating a simple daily motivation task: ```bash -curl -X POST "https://api.julep.ai/api/agents/YOUR_AGENT_ID/tasks" \ +curl -X POST "https://dev.julep.ai/api/agents/YOUR_AGENT_ID/tasks" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -96,4 +96,4 @@ For tasks that can benefit from parallel processing, use the `parallel` step: ## Next Steps - Learn about [handling executions](./handling_executions.md) to manage and monitor your tasks. -- Explore [integrating tools](../tutorials/integrating_tools.md) to enhance your task capabilities. \ No newline at end of file +- Explore [integrating tools](../tutorials/integrating_tools.md) to enhance your task capabilities. diff --git a/docs/how-to-guides/handling_executions.md b/docs/how-to-guides/handling_executions.md index 22aeedaa5..8b8ff5b2c 100644 --- a/docs/how-to-guides/handling_executions.md +++ b/docs/how-to-guides/handling_executions.md @@ -7,7 +7,7 @@ This guide covers how to manage and monitor task executions in Julep. To start a new execution of a task: ```bash -curl -X POST "https://api.julep.ai/api/tasks/YOUR_TASK_ID/executions" \ +curl -X POST "https://dev.julep.ai/api/tasks/YOUR_TASK_ID/executions" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -24,7 +24,7 @@ curl -X POST "https://api.julep.ai/api/tasks/YOUR_TASK_ID/executions" \ To check the status of an execution: ```bash -curl -X GET "https://api.julep.ai/api/executions/YOUR_EXECUTION_ID" \ +curl -X GET "https://dev.julep.ai/api/executions/YOUR_EXECUTION_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" ``` @@ -33,7 +33,7 @@ curl -X GET "https://api.julep.ai/api/executions/YOUR_EXECUTION_ID" \ If an execution is in the "awaiting_input" state, you can resume it with: ```bash -curl -X POST "https://api.julep.ai/api/executions/resume" \ +curl -X POST "https://dev.julep.ai/api/executions/resume" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -49,7 +49,7 @@ curl -X POST "https://api.julep.ai/api/executions/resume" \ To cancel a running execution: ```bash -curl -X PUT "https://api.julep.ai/api/executions/YOUR_EXECUTION_ID" \ +curl -X PUT "https://dev.julep.ai/api/executions/YOUR_EXECUTION_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -63,10 +63,10 @@ To stream events from an execution in real-time: ```bash curl -N -H "Authorization: Bearer $JULEP_API_KEY" \ - "https://api.julep.ai/api/executions/YOUR_EXECUTION_ID/transitions/stream" + "https://dev.julep.ai/api/executions/YOUR_EXECUTION_ID/transitions/stream" ``` ## Next Steps - Learn about [customizing tasks](./customizing_tasks.md) to create more complex workflows. -- Explore [using chat features](./using_chat_features.md) to interact with agents during task execution. \ No newline at end of file +- Explore [using chat features](./using_chat_features.md) to interact with agents during task execution. diff --git a/docs/how-to-guides/managing_users.md b/docs/how-to-guides/managing_users.md index 6bdb94bac..1337579b7 100644 --- a/docs/how-to-guides/managing_users.md +++ b/docs/how-to-guides/managing_users.md @@ -7,7 +7,7 @@ This guide covers how to create, update, and delete users in Julep. To create a new user: ```bash -curl -X POST "https://api.julep.ai/api/users" \ +curl -X POST "https://dev.julep.ai/api/users" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -21,7 +21,7 @@ curl -X POST "https://api.julep.ai/api/users" \ To update an existing user: ```bash -curl -X PUT "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X PUT "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -35,7 +35,7 @@ curl -X PUT "https://api.julep.ai/api/users/YOUR_USER_ID" \ To delete a user: ```bash -curl -X DELETE "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X DELETE "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" ``` @@ -44,11 +44,11 @@ curl -X DELETE "https://api.julep.ai/api/users/YOUR_USER_ID" \ To get information about a specific user: ```bash -curl -X GET "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X GET "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" ``` ## Next Steps - Learn about [managing sessions](../tutorials/managing_sessions.md) with users. -- Explore [using chat features](./using_chat_features.md) to interact with agents as a user. \ No newline at end of file +- Explore [using chat features](./using_chat_features.md) to interact with agents as a user. diff --git a/docs/how-to-guides/using_chat_features.md b/docs/how-to-guides/using_chat_features.md index 63423ff0c..6de3a2901 100644 --- a/docs/how-to-guides/using_chat_features.md +++ b/docs/how-to-guides/using_chat_features.md @@ -7,7 +7,7 @@ This guide covers how to use the chat features in Julep for dynamic interactions To start a new chat session: ```bash -curl -X POST "https://api.julep.ai/api/sessions" \ +curl -X POST "https://dev.julep.ai/api/sessions" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -21,7 +21,7 @@ curl -X POST "https://api.julep.ai/api/sessions" \ To send a message in a chat session: ```bash -curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/chat" \ +curl -X POST "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/chat" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -52,7 +52,7 @@ curl -N -H "Authorization: Bearer $JULEP_API_KEY" \ ], "stream": true }' \ - "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/chat" + "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/chat" ``` ## Using Tools in Chat @@ -60,7 +60,7 @@ curl -N -H "Authorization: Bearer $JULEP_API_KEY" \ To use a tool during a chat session: ```bash -curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/chat" \ +curl -X POST "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/chat" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -94,4 +94,4 @@ curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/chat" \ ## Next Steps - Explore [customizing tasks](./customizing_tasks.md) to create more complex interactions. -- Learn about [handling executions](./handling_executions.md) for long-running processes. \ No newline at end of file +- Learn about [handling executions](./handling_executions.md) for long-running processes. diff --git a/docs/introduction/getting_started.md b/docs/introduction/getting_started.md index 6aa590121..3ddeb77e5 100644 --- a/docs/introduction/getting_started.md +++ b/docs/introduction/getting_started.md @@ -20,7 +20,7 @@ export JULEP_API_KEY=your_api_key_here 3. Test your setup with a simple API call: ```bash -curl -H "Authorization: Bearer $JULEP_API_KEY" https://api.julep.ai/api/agents +curl -H "Authorization: Bearer $JULEP_API_KEY" https://dev.julep.ai/api/agents ``` If successful, you should receive a list of agents (or an empty list if you haven't created any yet). @@ -29,4 +29,4 @@ If successful, you should receive a list of agents (or an empty list if you have - Create your first agent using the [Creating Your First Agent](../tutorials/creating_your_first_agent.md) tutorial. - Explore the [API Reference](../reference/api_endpoints/) to learn about available endpoints. -- Check out the [How-to Guides](../how-to-guides/) for specific tasks and use cases. \ No newline at end of file +- Check out the [How-to Guides](../how-to-guides/) for specific tasks and use cases. diff --git a/docs/julep-concepts.md b/docs/julep-concepts.md index b6fc945d7..737a1f9cc 100644 --- a/docs/julep-concepts.md +++ b/docs/julep-concepts.md @@ -1,4 +1,5 @@ # Julep Concepts +

Table of Contents

@@ -49,11 +50,11 @@ - [Execution Endpoints](#execution-endpoints) - [Execution Models](#execution-models) - [Execution State Transitions](#execution-state-transitions) -
+ -***** +--- -**New feature announced! _🌟Tasks🌟_** +**New feature announced! _🌟Tasks🌟_** -***** +--- ## Agent @@ -114,14 +115,16 @@ Additionally, there are related endpoints for managing agent documents, tools, a Here are some curl command examples for the main Agent endpoints: 1. List Agents (paginated): + ```bash -curl -X GET "https://api.julep.ai/api/agents?limit=10&offset=0" \ +curl -X GET "https://dev.julep.ai/api/agents?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 2. Create a new Agent: + ```bash -curl -X POST "https://api.julep.ai/api/agents" \ +curl -X POST "https://dev.julep.ai/api/agents" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -133,14 +136,16 @@ curl -X POST "https://api.julep.ai/api/agents" \ ``` 3. Get an Agent by id: + ```bash -curl -X GET "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ +curl -X GET "https://dev.julep.ai/api/agents/YOUR_AGENT_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 4. Update an existing Agent by id (PUT): + ```bash -curl -X PUT "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ +curl -X PUT "https://dev.julep.ai/api/agents/YOUR_AGENT_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -152,8 +157,9 @@ curl -X PUT "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ ``` 5. Update an existing Agent by id (PATCH): + ```bash -curl -X PATCH "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ +curl -X PATCH "https://dev.julep.ai/api/agents/YOUR_AGENT_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -162,14 +168,15 @@ curl -X PATCH "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ ``` 6. Delete Agent by id: + ```bash -curl -X DELETE "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \ +curl -X DELETE "https://dev.julep.ai/api/agents/YOUR_AGENT_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Remember to replace `YOUR_API_KEY` with your actual Julep API key and `YOUR_AGENT_ID` with the specific agent ID you're working with. -***** +--- ## User @@ -204,14 +211,16 @@ Additionally, there are related endpoints for managing user documents: Here are some curl command examples for the main User endpoints: 1. List Users (paginated): + ```bash -curl -X GET "https://api.julep.ai/api/users?limit=10&offset=0" \ +curl -X GET "https://dev.julep.ai/api/users?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 2. Create a new User: + ```bash -curl -X POST "https://api.julep.ai/api/users" \ +curl -X POST "https://dev.julep.ai/api/users" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -221,14 +230,16 @@ curl -X POST "https://api.julep.ai/api/users" \ ``` 3. Get a User by id: + ```bash -curl -X GET "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X GET "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 4. Update an existing User by id (PUT): + ```bash -curl -X PUT "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X PUT "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -238,8 +249,9 @@ curl -X PUT "https://api.julep.ai/api/users/YOUR_USER_ID" \ ``` 5. Update an existing User by id (PATCH): + ```bash -curl -X PATCH "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X PATCH "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -248,24 +260,26 @@ curl -X PATCH "https://api.julep.ai/api/users/YOUR_USER_ID" \ ``` 6. Delete User by id: + ```bash -curl -X DELETE "https://api.julep.ai/api/users/YOUR_USER_ID" \ +curl -X DELETE "https://dev.julep.ai/api/users/YOUR_USER_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Remember to replace `YOUR_API_KEY` with your actual Julep API key and `YOUR_USER_ID` with the specific user ID you're working with. -***** +--- ## Session `Session` is the main workhorse for julep apps: + - You interact with agents inside sessions. You can create multiple sessions per agent. - Each session maintains its own context for sending to the agent's model. -- A session can have *one or more* agents and *zero or more* users associated with it. +- A session can have _one or more_ agents and _zero or more_ users associated with it. - You can control what happens when the history exceeds the context window limit using `context_overflow` setting. -A `Session` consists of: +A `Session` consists of: | **Field** | **Description** | | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -294,14 +308,16 @@ The Session API provides the following endpoints: Here are some curl command examples for the main Session endpoints: 1. List Sessions (paginated): + ```bash -curl -X GET "https://api.julep.ai/api/sessions?limit=10&offset=0" \ +curl -X GET "https://dev.julep.ai/api/sessions?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 2. Create a new Session: + ```bash -curl -X POST "https://api.julep.ai/api/sessions" \ +curl -X POST "https://dev.julep.ai/api/sessions" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -314,14 +330,16 @@ curl -X POST "https://api.julep.ai/api/sessions" \ ``` 3. Get a Session by id: + ```bash -curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ +curl -X GET "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 4. Update an existing Session by id: + ```bash -curl -X PUT "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ +curl -X PUT "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -332,20 +350,23 @@ curl -X PUT "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ ``` 5. Delete Session by id: + ```bash -curl -X DELETE "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ +curl -X DELETE "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 6. Get Session Messages: + ```bash -curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ +curl -X GET "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 7. Create a new Message in a Session: + ```bash -curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ +curl -X POST "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -355,8 +376,9 @@ curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ ``` 8. Get Session Tools: + ```bash -curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/tools" \ +curl -X GET "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/tools" \ -H "Authorization: Bearer YOUR_API_KEY" ``` @@ -365,6 +387,7 @@ Remember to replace `YOUR_API_KEY`, `YOUR_AGENT_ID`, `YOUR_USER_ID`, and `YOUR_S ### `metadata` precedence order In julep, the following objects can have `metadata` added to them: + - `Agent` - `User` - `Session` @@ -373,25 +396,27 @@ In julep, the following objects can have `metadata` added to them: - `Execution` Whenever multiple objects with the same `metadata` field are present in a scope, the value takes the following precedence (from highest to lowest): + - In a session: `session > user > agent` - During a task execution: `execution > task > agent` ### Context overflow Whenever the context size grows beyond the `token_budget` or the model's input limit, the backend figures out what to do next based on the `context_overflow` setting: + - `null`: Raise an exception. The client is responsible for creating a new session or clearing the history for the current one. - `"truncate"`: Truncate the context from the top except the for system prompt until the size falls below the budget. Raises an error if system prompt and last message combined exceed the budget. - `"adaptive"`: Whenever the context size reaches `75%` of the `token_budget`, a background task is created to compress the information by summarizing, merging and clipping messages in the context. This is done on a best effort basis. Requests might fail if the context wasn't compressed enough or on time. ### Default system template -```jinja +```jinja {%- if agent.name -%} You are {{agent.name}}.{{" "}} {%- endif -%} {%- if agent.about -%} -About you: {{agent.name}}.{{" "}} +About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -447,6 +472,7 @@ Relevant documents:{{"\n"}} ## Multi-agent sessions There are different types of sessions based on the number of agents and users: + - Single agent: No user, single user, or multiple users - Multiple agents: No user, single user, or multiple users @@ -462,7 +488,7 @@ A session can have more than one agents or users. The session's behavior changes **Multiple agents**: When a message is received by the session, each agent is called one after another in the order they were defined in the session. You can also specify which `agent` to use in a request, in which case, just that agent will be used. -***** +--- ## Chat @@ -497,7 +523,8 @@ Additional settings are available for fine-tuning the output, such as `temperatu The chat response can be either streamed or returned as a complete message: -1. **Streamed Response**: +1. **Streamed Response**: + - Content-Type: `text/event-stream` - Body: A stream of `ChatOutputChunk` objects. @@ -506,6 +533,7 @@ The chat response can be either streamed or returned as a complete message: - Body: A `MessageChatResponse` object containing the full generated message(s). Both response types include: + - `usage`: Token usage statistics. - `jobs`: Background job IDs spawned from this interaction. - `docs`: Documents referenced for this request (for citation purposes). @@ -546,7 +574,7 @@ This endpoint allows you to send messages to an agent and receive responses. Here's a basic curl command to interact with the chat endpoint: ```bash -curl -X POST "https://api.julep.ai/api/sessions/{SESSION_ID}/chat" \ +curl -X POST "https://dev.julep.ai/api/sessions/{SESSION_ID}/chat" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -563,7 +591,7 @@ curl -X POST "https://api.julep.ai/api/sessions/{SESSION_ID}/chat" \ Remember to replace `{SESSION_ID}` with your actual session ID and `YOUR_API_KEY` with your Julep API key. -***** +--- ## Tool @@ -572,40 +600,42 @@ Agents can be given access to a number of "tools" -- any programmatic interface Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. Tools in julep can be one of: + 1. User-defined `function`s - These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: - ```yaml - name: send_text_message - description: Send a text message to a recipient. - parameters: - type: object - properties: - to: - type: string - description: Phone number of recipient. - text: - type: string - description: Content of the message. - ``` + These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + + ```yaml + name: send_text_message + description: Send a text message to a recipient. + parameters: + type: object + properties: + to: + type: string + description: Phone number of recipient. + text: + type: string + description: Content of the message. + ``` 2. `system` tools (upcoming) - Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. - - `system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. - + Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + + `system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + 3. Built-in `integration`s (upcoming) - julep backend ships with integrated third party tools from the following providers: - - [composio](https://composio.dev) \*\* - - [anon](https://anon.com) \*\* - - [langchain toolkits](https://python.langchain.com/v0.2/docs/integrations/toolkits/). Support for _Github, Gitlab, Gmail, Jira, MultiOn, Slack_ toolkits is planned. + julep backend ships with integrated third party tools from the following providers: - \*\* Since _composio_ and _anon_ are third-party providers, their tools require setting up account linking. + - [composio](https://composio.dev) \*\* + - [anon](https://anon.com) \*\* + - [langchain toolkits](https://python.langchain.com/v0.2/docs/integrations/toolkits/). Support for _Github, Gitlab, Gmail, Jira, MultiOn, Slack_ toolkits is planned. - `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + \*\* Since _composio_ and _anon_ are third-party providers, their tools require setting up account linking. -4. Webhooks & `api_call`s (upcoming) - julep can build natural-language tools from openapi specs. Under the hood, we use [langchain's NLA toolkit](https://python.langchain.com/v0.2/docs/integrations/toolkits/openapi_nla/) for this. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. +4. Webhooks & `api_call`s (upcoming) + julep can build natural-language tools from openapi specs. Under the hood, we use [langchain's NLA toolkit](https://python.langchain.com/v0.2/docs/integrations/toolkits/openapi_nla/) for this. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. ### Tool Endpoints @@ -622,14 +652,16 @@ The Tool API provides the following endpoints for managing tools associated with Here are some curl command examples for the main Tool endpoints: 1. List Tools (paginated): + ```bash -curl -X GET "https://api.julep.ai/api/agents/{agent_id}/tools?limit=10&offset=0" \ +curl -X GET "https://dev.julep.ai/api/agents/{agent_id}/tools?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` 2. Create a new Tool: + ```bash -curl -X POST "https://api.julep.ai/api/agents/{agent_id}/tools" \ +curl -X POST "https://dev.julep.ai/api/agents/{agent_id}/tools" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -652,8 +684,9 @@ curl -X POST "https://api.julep.ai/api/agents/{agent_id}/tools" \ ``` 3. Update an existing Tool (PUT): + ```bash -curl -X PUT "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ +curl -X PUT "https://dev.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -680,8 +713,9 @@ curl -X PUT "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ ``` 4. Update an existing Tool (PATCH): + ```bash -curl -X PATCH "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ +curl -X PATCH "https://dev.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -692,18 +726,19 @@ curl -X PATCH "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ ``` 5. Delete Tool: + ```bash -curl -X DELETE "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ +curl -X DELETE "https://dev.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Remember to replace `YOUR_API_KEY`, `{agent_id}`, and `{tool_id}` with your actual Julep API key, agent ID, and tool ID respectively. - ### Partial application of arguments to tools Often, it's necessary to _partial_ some arguments of a particular tool. You can do that by setting the `x-integration-args` field on the `metadata` of the required scope. For instance, say you have the following user-defined function tool: -```yaml + +```yaml name: check_account_status description: Get the account status for a customer parameters: @@ -715,6 +750,7 @@ parameters: ``` When chatting with a particular user, the `customer_id` field is expected to be fixed. In this case, you can set it on the `User` using: + ```json { "metadata": { @@ -733,6 +769,7 @@ The convention for naming the fields for that object is `": | null` | JSON schema to validate input when executing the task (default: null). | -| `tools` | `TaskTool[]` | Additional tools specific to this task (default: empty array). | -| `inherit_tools` | `boolean` | Whether to inherit tools from the parent agent (default: true). | +| **Field** | **Type** | **Description** | +| :-------------- | :--------------- | :-------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `name` | `string` | The name of the task (required for creation). | +| `description` | `string` | A description of the task (default: ""). | +| `main` | `WorkflowStep[]` | The main workflow steps (required, minimum 1 item). | +| `input_schema` | `Record | null` | JSON schema to validate input when executing the task (default: null). | +| `tools` | `TaskTool[]` | Additional tools specific to this task (default: empty array). | +| `inherit_tools` | `boolean` | Whether to inherit tools from the parent agent (default: true). | Additionally, a `Task` includes: + - An `id` (UUID, read-only) - Timestamps (`created_at` and `updated_at`, read-only) - Metadata (key-value pairs) @@ -872,9 +917,9 @@ Additionally, a `Task` includes: A `TaskTool` extends the `CreateToolRequest` and includes: -| **Field** | **Type** | **Description** | -|:-------------|:------------|:-----------------------------------------------------------| -| `inherited` | `boolean` | Whether the tool was inherited (read-only, default: false) | +| **Field** | **Type** | **Description** | +| :---------- | :-------- | :--------------------------------------------------------- | +| `inherited` | `boolean` | Whether the tool was inherited (read-only, default: false) | ### Example task definition @@ -899,51 +944,52 @@ input_schema: required: ["about_user", "topics", "user_email"] tools: -- function: - name: send_email - description: Sends an email to the user - parameters: - type: object - properties: - subject: - type: string - content: - type: string - recipient: - type: string - required: ["subject", "content", "recipient"] + - function: + name: send_email + description: Sends an email to the user + parameters: + type: object + properties: + subject: + type: string + content: + type: string + recipient: + type: string + required: ["subject", "content", "recipient"] main: -# Pick a random topic. -# `evaluate` step takes a key-value object where the values are valid python *expressions*. -- evaluate: - chosen_topic: _["topics"][randint(len(_["topics"]))] - -# Think about what support the user might need. -# Note: `inputs` and `outputs` are globals. -- prompt: You are a motivational coach and you are coaching someone who is {{inputs[0]["about_user"]}}. Think of the challenges they might be facing on the {{_["chosen_topic"]}} topic and what to do about them. Write down your answer as a bulleted list. - -# Write a poem about it. -# Note: `_` stands for `outputs[-1]` i.e. the last step's output -- prompt: Write a short motivational poem about {{_["choices"][0].content}} - -# Manually call the send_email function. -# `arguments` is an object where values are python expressions. -- tool: - name: send_email - arguments: - subject: '"Daily Motivation"' - content: _["choices"][0].content - -# Sleep for a day -- sleep: 24*3600 - -# Start all over again -- workflow: main - arguments: inputs[0] + # Pick a random topic. + # `evaluate` step takes a key-value object where the values are valid python *expressions*. + - evaluate: + chosen_topic: _["topics"][randint(len(_["topics"]))] + + # Think about what support the user might need. + # Note: `inputs` and `outputs` are globals. + - prompt: You are a motivational coach and you are coaching someone who is {{inputs[0]["about_user"]}}. Think of the challenges they might be facing on the {{_["chosen_topic"]}} topic and what to do about them. Write down your answer as a bulleted list. + + # Write a poem about it. + # Note: `_` stands for `outputs[-1]` i.e. the last step's output + - prompt: Write a short motivational poem about {{_["choices"][0].content}} + + # Manually call the send_email function. + # `arguments` is an object where values are python expressions. + - tool: + name: send_email + arguments: + subject: '"Daily Motivation"' + content: _["choices"][0].content + + # Sleep for a day + - sleep: 24*3600 + + # Start all over again + - workflow: main + arguments: inputs[0] ``` This example demonstrates a daily motivation task that: + 1. Selects a random topic and gets the current date. 2. Generates motivational content based on the user's profile and chosen topic. 3. Creates a motivational poem based on the generated content. @@ -952,43 +998,46 @@ This example demonstrates a daily motivation task that: ### Types of workflow steps -| **Step Type** | **Description** | -|:----------------| :----------------------------------------------------------------------------------------------------------------------------------------------------| -| Tool Call | Runs a specified tool with given arguments. | -| Prompt | Runs a prompt using a model. You can override settings and interpolate variables using [jinja](https://jinja.palletsprojects.com/) templates. | -| Evaluate | Accepts an object with values that are valid python expressions. The step runs the expressions and the result becomes the output of this step. | -| Wait for input | Suspends the execution and waits for the caller to resume execution with an input. | -| Log | Logs information during the workflow execution. | -| Embed | Embeds text for semantic operations. | -| Search | Searches for documents in the agent's doc store. | -| Set | Sets a value in the workflow's key-value store. | -| Get | Retrieves a value from the workflow's key-value store. | -| Foreach | Runs a step for every value from a list in serial order. | -| Map-reduce | Runs a step for every value of the input list in parallel. Requires a reduce expression to collect the results. | -| Parallel | Executes multiple steps in parallel. | -| Switch | Executes different steps based on a condition, similar to a switch statement in programming. | -| If-else | Conditional step where the `if` field expression is evaluated. If the output is truthy then the `then` branch is executed, otherwise `else` branch. | -| Sleep | Pauses the workflow execution for a specified number of seconds. | -| Return | Ends the current workflow and optionally returns a value. | -| Yield | Switches to another named workflow. Can add custom inputs (Default: output of previous steps). | -| Error | Throws an error with the provided message and exits the workflow. | +| **Step Type** | **Description** | +| :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| Tool Call | Runs a specified tool with given arguments. | +| Prompt | Runs a prompt using a model. You can override settings and interpolate variables using [jinja](https://jinja.palletsprojects.com/) templates. | +| Evaluate | Accepts an object with values that are valid python expressions. The step runs the expressions and the result becomes the output of this step. | +| Wait for input | Suspends the execution and waits for the caller to resume execution with an input. | +| Log | Logs information during the workflow execution. | +| Embed | Embeds text for semantic operations. | +| Search | Searches for documents in the agent's doc store. | +| Set | Sets a value in the workflow's key-value store. | +| Get | Retrieves a value from the workflow's key-value store. | +| Foreach | Runs a step for every value from a list in serial order. | +| Map-reduce | Runs a step for every value of the input list in parallel. Requires a reduce expression to collect the results. | +| Parallel | Executes multiple steps in parallel. | +| Switch | Executes different steps based on a condition, similar to a switch statement in programming. | +| If-else | Conditional step where the `if` field expression is evaluated. If the output is truthy then the `then` branch is executed, otherwise `else` branch. | +| Sleep | Pauses the workflow execution for a specified number of seconds. | +| Return | Ends the current workflow and optionally returns a value. | +| Yield | Switches to another named workflow. Can add custom inputs (Default: output of previous steps). | +| Error | Throws an error with the provided message and exits the workflow. | #### Tool Call + Runs a specified tool with given arguments. ```yaml tool: send_email arguments: - subject: "f'Daily Motivation: {_[\"chosen_topic\"]}'" + subject: 'f''Daily Motivation: {_["chosen_topic"]}''' content: "_['choices'][0].content" recipient: "inputs['user_email']" ``` In this example: + - `tool` is a string reference to the tool to be called. - `arguments` is an object where each value is a Python expression (TypedExpression). These expressions are evaluated at runtime. #### Prompt + Runs a prompt using a model. You can override settings and interpolate variables using [jinja](https://jinja.palletsprojects.com/) templates. ```yaml @@ -999,10 +1048,12 @@ settings: ``` This example shows: + - A `prompt` using Jinja template syntax for variable interpolation. - `settings` to override default chat parameters for this specific prompt. #### Evaluate + Accepts an object with values that are valid Python expressions. The step runs the expressions and the result becomes the output of this step. ```yaml @@ -1015,6 +1066,7 @@ evaluate: Each value in the `evaluate` object is a Python expression (TypedExpression) that gets evaluated. #### Wait for input + Suspends the execution and waits for the caller to resume execution with an input. ```yaml @@ -1028,6 +1080,7 @@ wait_for_input: The `info` object contains expressions that will be evaluated and provided to the user interface. #### Log + Logs information during the workflow execution. ```yaml @@ -1037,6 +1090,7 @@ log: "Generated motivation for topic: {{_['chosen_topic']}}" The `log` field uses a Jinja template for variable interpolation. #### Embed + Embeds text for semantic operations. ```yaml @@ -1048,6 +1102,7 @@ embed: This step embeds the content generated in a previous step. #### Search + Searches for documents in the agent's doc store. ```yaml @@ -1059,6 +1114,7 @@ search: This step searches for documents related to the chosen topic. #### Set + Sets a value in the workflow's key-value store. ```yaml @@ -1070,6 +1126,7 @@ set: This step sets or updates values in the workflow's key-value store. #### Get + Retrieves a value from the workflow's key-value store. ```yaml @@ -1079,6 +1136,7 @@ get: last_topic This step retrieves the value of "last_topic" from the key-value store. #### Foreach + Runs a step for every value from a list in serial order. ```yaml @@ -1091,6 +1149,7 @@ foreach: This step iterates over the input topics, generating a tip for each one. #### Map-reduce + Runs a step for every value of the input list in parallel. Requires a reduce expression to collect the results. ```yaml @@ -1105,6 +1164,7 @@ parallelism: 5 This step generates quotes for multiple topics in parallel and then combines them into a single string. #### Parallel + Executes multiple steps in parallel. ```yaml @@ -1118,6 +1178,7 @@ parallel: This step runs a prompt and a tool call in parallel. #### Switch + Executes different steps based on a condition, similar to a switch statement in programming. ```yaml @@ -1136,6 +1197,7 @@ switch: This step chooses different actions based on the number of previous responses. #### If-else + Conditional step where the `if` field expression is evaluated. If the output is truthy then the `then` branch is executed, otherwise `else` branch. ```yaml @@ -1149,6 +1211,7 @@ else: This step chooses different prompts based on the user's mood. #### Sleep + Pauses the workflow execution for a specified number of seconds. ```yaml @@ -1162,6 +1225,7 @@ sleep: This step pauses the workflow for 24 hours. #### Return + Ends the current workflow and optionally returns a value. ```yaml @@ -1173,6 +1237,7 @@ return: This step ends the workflow and returns the generated motivation message along with a timestamp. #### Yield + Switches to another named workflow. Can add custom inputs (Default: output of previous steps). ```yaml @@ -1185,6 +1250,7 @@ arguments: This step switches to a "generate_weekly_summary" workflow, passing in recent topics and the user profile. #### Error + Throws an error with the provided message and exits the workflow. ```yaml @@ -1195,7 +1261,7 @@ This step throws an error and terminates the workflow execution. These expanded examples provide a more comprehensive look at how each task step can be used in a Julep workflow, showcasing the flexibility and power of the task system. -***** +--- ## Execution @@ -1268,8 +1334,8 @@ stateDiagram-v2 | +-------------+ +---------->| running | +-------------+ - | | - | | + | | + | | +----v---v----+ | succeeded | +-------------+ @@ -1353,4 +1419,4 @@ Each transition type corresponds to a specific execution status: This state transition model ensures that executions follow a consistent and predictable flow, allowing for complex workflows while maintaining clear status tracking. -***** +--- diff --git a/docs/reference/api_endpoints/tool_endpoints.md b/docs/reference/api_endpoints/tool_endpoints.md index a0aeab36a..2dc2e36ce 100644 --- a/docs/reference/api_endpoints/tool_endpoints.md +++ b/docs/reference/api_endpoints/tool_endpoints.md @@ -1,5 +1,6 @@ # Tool Endpoints + This document provides a reference for all Tool API endpoints in Julep. ## List Tools for an Agent diff --git a/docs/reference/api_endpoints/user_endpoints.md b/docs/reference/api_endpoints/user_endpoints.md index 9242c7167..d8e59ce46 100644 --- a/docs/reference/api_endpoints/user_endpoints.md +++ b/docs/reference/api_endpoints/user_endpoints.md @@ -1,5 +1,6 @@ # User Endpoints + This document provides a reference for all User API endpoints in Julep. ## List Users diff --git a/docs/tutorials/creating_your_first_agent.md b/docs/tutorials/creating_your_first_agent.md index a21d27b1d..506917eeb 100644 --- a/docs/tutorials/creating_your_first_agent.md +++ b/docs/tutorials/creating_your_first_agent.md @@ -20,7 +20,7 @@ Decide on the basic properties of your agent: Use the following curl command to create your agent: ```bash -curl -X POST "https://api.julep.ai/api/agents" \ +curl -X POST "https://dev.julep.ai/api/agents" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -36,7 +36,7 @@ curl -X POST "https://api.julep.ai/api/agents" \ Check if your agent was created successfully: ```bash -curl -X GET "https://api.julep.ai/api/agents" \ +curl -X GET "https://dev.julep.ai/api/agents" \ -H "Authorization: Bearer $JULEP_API_KEY" ``` @@ -45,4 +45,4 @@ You should see your newly created agent in the list. ## Next Steps - Learn how to [manage sessions](./managing_sessions.md) with your new agent. -- Explore [integrating tools](./integrating_tools.md) to enhance your agent's capabilities. \ No newline at end of file +- Explore [integrating tools](./integrating_tools.md) to enhance your agent's capabilities. diff --git a/docs/tutorials/integrating_tools.md b/docs/tutorials/integrating_tools.md index 16889f4a6..b82933821 100644 --- a/docs/tutorials/integrating_tools.md +++ b/docs/tutorials/integrating_tools.md @@ -7,7 +7,7 @@ This tutorial will show you how to integrate tools with your Julep agents. Here's how to create a simple tool for sending emails: ```bash -curl -X POST "https://api.julep.ai/api/agents/YOUR_AGENT_ID/tools" \ +curl -X POST "https://dev.julep.ai/api/agents/YOUR_AGENT_ID/tools" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -42,7 +42,7 @@ curl -X POST "https://api.julep.ai/api/agents/YOUR_AGENT_ID/tools" \ When creating or updating a session, you can specify which tools to use: ```bash -curl -X POST "https://api.julep.ai/api/sessions" \ +curl -X POST "https://dev.julep.ai/api/sessions" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -55,4 +55,4 @@ curl -X POST "https://api.julep.ai/api/sessions" \ ## Next Steps - Learn about [customizing tasks](../how-to-guides/customizing_tasks.md) to create complex workflows using tools. -- Explore [handling executions](../how-to-guides/handling_executions.md) to manage tool usage in tasks. \ No newline at end of file +- Explore [handling executions](../how-to-guides/handling_executions.md) to manage tool usage in tasks. diff --git a/docs/tutorials/managing_sessions.md b/docs/tutorials/managing_sessions.md index 866484912..15fb52c49 100644 --- a/docs/tutorials/managing_sessions.md +++ b/docs/tutorials/managing_sessions.md @@ -1,5 +1,6 @@ # Managing Sessions + This tutorial will guide you through creating and managing sessions with your Julep agents. ## Creating a Session @@ -7,7 +8,7 @@ This tutorial will guide you through creating and managing sessions with your Ju To create a new session with an agent: ```bash -curl -X POST "https://api.julep.ai/api/sessions" \ +curl -X POST "https://dev.julep.ai/api/sessions" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -22,7 +23,7 @@ curl -X POST "https://api.julep.ai/api/sessions" \ To send a message in a session: ```bash -curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ +curl -X POST "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -36,7 +37,7 @@ curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \ If you're dealing with long conversations, you may need to handle context overflow: ```bash -curl -X PUT "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ +curl -X PUT "https://dev.julep.ai/api/sessions/YOUR_SESSION_ID" \ -H "Authorization: Bearer $JULEP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ @@ -48,4 +49,4 @@ curl -X PUT "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \ ## Next Steps - Learn about [integrating tools](./integrating_tools.md) to enhance your sessions. -- Explore [customizing tasks](../how-to-guides/customizing_tasks.md) for more complex interactions. \ No newline at end of file +- Explore [customizing tasks](../how-to-guides/customizing_tasks.md) for more complex interactions. diff --git a/example.js b/example.js new file mode 100644 index 000000000..6bbc083a2 --- /dev/null +++ b/example.js @@ -0,0 +1,161 @@ +// Step 0: Setup +const dotenv = require('dotenv'); +const { Julep } = require('@julep/sdk'); +const yaml = require('yaml'); + +dotenv.config(); + +const client = new Julep({ apiKey: process.env.JULEP_API_KEY, environment: process.env.JULEP_ENVIRONMENT || "production" }); + +// Step 1: Create an Agent +async function createAgent() { + const agent = await client.agents.create({ + name: "Storytelling Agent", + model: "claude-3.5-sonnet", + about: "You are a creative storyteller that crafts engaging stories on a myriad of topics.", + }); + return agent; +} + +// Step 2: Create a Task that generates a story and comic strip +const taskYaml = ` +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + integration: + provider: wikipedia + method: search + +main: + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside \`\`\`yaml tags at the end of your response. + unwrap: true + + - evaluate: + plot_ideas: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + + # Step 2: Extract research fields from the plot ideas + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Here are some plot ideas for a story: + {% for idea in _.plot_ideas %} + - {{idea}} + {% endfor %} + + To develop the story, we need to research for the plot ideas. + What should we research? Write down wikipedia search queries for the plot ideas you think are interesting. + Return your output as a yaml list inside \`\`\`yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 + + - evaluate: + research_queries: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + + # Step 3: Research each plot idea + - foreach: + in: _.research_queries + do: + tool: research_wikipedia + arguments: + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + + # Step 4: Think and deliberate + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside \`\`\`yaml tags at the end of your response. The yaml object should have the following structure: + + \`\`\`yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""\`\`\` + + Make sure the yaml is valid and the characters and scenes are not empty. Also take care of semicolons and other gotchas of writing yaml. + unwrap: true + + - evaluate: + plot: "load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip())" +`; + +async function createTask(agentId) { + const task = await client.tasks.create( + agentId, + yaml.parse(taskYaml) + ); + return task; +} + +// Step 3: Execute the Task +async function executeTask(taskId) { + const execution = await client.executions.create(taskId, { + input: { idea: "A cat who learns to fly" } + }); + + // 🎉 Watch as the story and comic panels are generated + while (true) { + const result = await client.executions.get(execution.id); + console.log(result.status, result.output); + + if (result.status === 'succeeded' || result.status === 'failed') { + // 📦 Once the execution is finished, retrieve the results + if (result.status === "succeeded") { + console.log(result.output); + } else { + throw new Error(result.error); + } + break; + } + + await new Promise(resolve => setTimeout(resolve, 1000)); + } +} + +// Main function to run the example +async function main() { + try { + const agent = await createAgent(); + const task = await createTask(agent.id); + await executeTask(task.id); + } catch (error) { + console.error("An error occurred:", error); + } +} + +main().then(() => console.log("Done")).catch(console.error); diff --git a/example.mjs b/example.mjs new file mode 100644 index 000000000..2fec89dfd --- /dev/null +++ b/example.mjs @@ -0,0 +1,161 @@ +// Step 0: Setup +import dotenv from 'dotenv'; +import { Julep } from '@julep/sdk'; +import yaml from 'yaml'; + +dotenv.config(); + +const client = new Julep({ apiKey: process.env.JULEP_API_KEY, environment: process.env.JULEP_ENVIRONMENT || "production" }); + +// Step 1: Create an Agent +async function createAgent() { + const agent = await client.agents.create({ + name: "Storytelling Agent", + model: "claude-3.5-sonnet", + about: "You are a creative storyteller that crafts engaging stories on a myriad of topics.", + }); + return agent; +} + +// Step 2: Create a Task that generates a story and comic strip +const taskYaml = ` +name: Storyteller +description: Create a story based on an idea. + +tools: + - name: research_wikipedia + integration: + provider: wikipedia + method: search + +main: + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside \`\`\`yaml tags at the end of your response. + unwrap: true + + - evaluate: + plot_ideas: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + + # Step 2: Extract research fields from the plot ideas + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Here are some plot ideas for a story: + {% for idea in _.plot_ideas %} + - {{idea}} + {% endfor %} + + To develop the story, we need to research for the plot ideas. + What should we research? Write down wikipedia search queries for the plot ideas you think are interesting. + Return your output as a yaml list inside \`\`\`yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 + + - evaluate: + research_queries: load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip()) + + # Step 3: Research each plot idea + - foreach: + in: _.research_queries + do: + tool: research_wikipedia + arguments: + query: _ + + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + + # Step 4: Think and deliberate + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside \`\`\`yaml tags at the end of your response. The yaml object should have the following structure: + + \`\`\`yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""\`\`\` + + Make sure the yaml is valid and the characters and scenes are not empty. Also take care of semicolons and other gotchas of writing yaml. + unwrap: true + + - evaluate: + plot: "load_yaml(_.split('\`\`\`yaml')[1].split('\`\`\`')[0].strip())" +`; + +async function createTask(agentId) { + const task = await client.tasks.create( + agentId, + yaml.parse(taskYaml) + ); + return task; +} + +// Step 3: Execute the Task +async function executeTask(taskId) { + const execution = await client.executions.create(taskId, { + input: { idea: "A cat who learns to fly" } + }); + + // 🎉 Watch as the story and comic panels are generated + while (true) { + const result = await client.executions.get(execution.id); + console.log(result.status, result.output); + + if (result.status === 'succeeded' || result.status === 'failed') { + // 📦 Once the execution is finished, retrieve the results + if (result.status === "succeeded") { + console.log(result.output); + } else { + throw new Error(result.error); + } + break; + } + + await new Promise(resolve => setTimeout(resolve, 1000)); + } +} + +// Main function to run the example +async function main() { + try { + const agent = await createAgent(); + const task = await createTask(agent.id); + await executeTask(task.id); + } catch (error) { + console.error("An error occurred:", error); + } +} + +main().then(() => console.log("Done")).catch(console.error); diff --git a/example.py b/example.py index ef6d6f427..cc7982523 100644 --- a/example.py +++ b/example.py @@ -1,107 +1,137 @@ -from julep import Julep, AsyncJulep +### Step 0: Setup -# 🔑 Initialize the Julep client -# Or alternatively, use AsyncJulep for async operations -client = Julep(api_key="your_api_key") +import os +import time +import yaml +from julep import Julep # or AsyncJulep -################## -## 🤖 Agent 🤖 ## -################## +client = Julep(api_key=os.environ["JULEP_API_KEY"]) + +### Step 1: Create an Agent -# Create a research agent agent = client.agents.create( - name="Research Agent", - about="You are a research agent designed to handle research inquiries.", + name="Storytelling Agent", model="claude-3.5-sonnet", + about="You are a creative storyteller that crafts engaging stories on a myriad of topics.", ) -# 🔍 Add a web search tool to the agent -client.agents.tools.create( - agent_id=agent.id, - name="web_search", # Should be python valid variable name - description="Use this tool to research inquiries.", - integration={ - "provider": "brave", - "method": "search", - "setup": { - "api_key": "your_brave_api_key", - }, - }, -) +### Step 2: Create a Task that generates a story and comic strip -################# -## 💬 Chat 💬 ## -################# +task_yaml = """ +name: Storyteller +description: Create a story based on an idea. -# Start an interactive chat session with the agent -session = client.sessions.create( - agent_id=agent.id, - context_overflow="adaptive", # 🧠 Julep will dynamically compute the context window if needed -) +tools: + - name: research_wikipedia + integration: + provider: wikipedia + method: search + +main: + # Step 1: Generate plot idea + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', generate a list of 5 plot ideas. Go crazy and be as creative as possible. Return your output as a list of long strings inside ```yaml tags at the end of your response. + unwrap: true + + - evaluate: + plot_ideas: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + + # Step 2: Extract research fields from the plot ideas + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Here are some plot ideas for a story: + {% for idea in _.plot_ideas %} + - {{idea}} + {% endfor %} -# 🔄 Chat loop -while (user_input := input("You: ")) != "exit": - response = client.sessions.chat( - session_id=session.id, - message=user_input, - ) + To develop the story, we need to research for the plot ideas. + What should we research? Write down wikipedia search queries for the plot ideas you think are interesting. + Return your output as a yaml list inside ```yaml tags at the end of your response. + unwrap: true + settings: + model: gpt-4o-mini + temperature: 0.7 - print("Agent: ", response.choices[0].message.content) + - evaluate: + research_queries: load_yaml(_.split('```yaml')[1].split('```')[0].strip()) + # Step 3: Research each plot idea + - foreach: + in: _.research_queries + do: + tool: research_wikipedia + arguments: + query: _ -################# -## 📋 Task 📋 ## -################# + - evaluate: + wikipedia_results: 'NEWLINE.join([f"- {doc.metadata.title}: {doc.metadata.summary}" for item in _ for doc in item.documents])' + + # Step 4: Think and deliberate + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: |- + Before we write the story, let's think and deliberate. Here are some plot ideas: + {% for idea in outputs[1].plot_ideas %} + - {{idea}} + {% endfor %} + + Here are the results from researching the plot ideas on Wikipedia: + {{_.wikipedia_results}} + + Think about the plot ideas critically. Combine the plot ideas with the results from Wikipedia to create a detailed plot for a story. + Write down all your notes and thoughts. + Then finally write the plot as a yaml object inside ```yaml tags at the end of your response. The yaml object should have the following structure: + + ```yaml + title: "" + characters: + - name: "" + about: "" + synopsis: "" + scenes: + - title: "" + description: "" + characters: + - name: "" + role: "" + plotlines: + - ""``` + + Make sure the yaml is valid and the characters and scenes are not empty. Also take care of semicolons and other gotchas of writing yaml. + unwrap: true + + - evaluate: + plot: "load_yaml(_.split('```yaml')[1].split('```')[0].strip())" +""" -# Create a recurring research task for the agent task = client.tasks.create( agent_id=agent.id, - name="Research Task", - description="Research the given topic every 24 hours.", - # - # 🛠️ Task specific tools - tools=[ - { - "name": "send_email", - "description": "Send an email to the user with the results.", - "api_call": { - "method": "post", - "url": "https://api.sendgrid.com/v3/mail/send", - "headers": {"Authorization": "Bearer YOUR_SENDGRID_API_KEY"}, - }, - } - ], - # - # 🔢 Task main steps - main=[ - # - # Step 1: Research the topic - { - # `_` (underscore) variable refers to the previous step's output - # Here, it points to the topic input from the user - "prompt": "Look up topic '{{_.topic}}' and summarize the results.", - "tools": [{"ref": {"name": "web_search"}}], # 🔍 Use the web search tool from the agent - "unwrap": True, - }, - # - # Step 2: Send email with research results - { - "tool": "send_email", - "arguments": { - "subject": "Research Results", - "body": "'Here are the research results for today: ' + _.content", - "to": "inputs[0].email", # Reference the email from the user's input - }, - }, - # - # Step 3: Wait for 24 hours before repeating - {"sleep": "24 * 60 * 60"}, - ], + **yaml.safe_load(task_yaml) +) + +### Step 3: Execute the Task + +execution = client.executions.create( + task_id=task.id, + input={"idea": "A cat who learns to fly"} ) -# 🚀 Start the recurring task -client.executions.create(task_id=task.id, input={"topic": "Python"}) +# 🎉 Watch as the story and comic panels are generated +while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']: + print(result.status, result.output) + time.sleep(1) -# 🔁 This will run the task every 24 hours, -# research for the topic "Python", and -# send the results to the user's email +# 📦 Once the execution is finished, retrieve the results +if result.status == "succeeded": + print(result.output) +else: + raise Exception(result.error) \ No newline at end of file diff --git a/example.ts b/example.ts index 3ef4e1a91..df795dd5e 100644 --- a/example.ts +++ b/example.ts @@ -1,117 +1,149 @@ -import Julep from '@julep/sdk'; - -// 🔑 Initialize the Julep client -const client = new Julep({ - apiKey: 'your_api_key', - environment: 'production', // or 'dev' | 'local_multi_tenant' | 'local' -}); - -async function main() { - /* - * 🤖 Agent 🤖 - */ - - // Create a research agent - const agent = await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', { - name: 'Research Agent', - about: 'You are a research agent designed to handle research inquiries.', - model: 'claude-3.5-sonnet', +import { Julep } from '@julep/sdk'; +import yaml from 'js-yaml'; +import readline from 'readline'; + +// Add these type declarations at the top of the file +declare module '@julep/sdk'; +declare module 'js-yaml'; + +const client = new Julep({ apiKey: 'your_julep_api_key' }); + +interface Agent { + id: string; + // Add other properties as needed +} + +interface Task { + id: string; + // Add other properties as needed +} + +interface Execution { + id: string; + // Add other properties as needed +} + +async function createAgent(): Promise { + const agent = await client.agents.create({ + name: "Storytelling Agent", + model: "gpt-4", + about: "You are a creative storytelling agent that can craft engaging stories and generate comic panels based on ideas.", }); - // 🔍 Add a web search tool to the agent + // 🛠️ Add an image generation tool (DALL·E) to the agent await client.agents.tools.create(agent.id, { - name: 'web_search', - description: 'Use this tool to research inquiries.', + name: "image_generator", + description: "Use this tool to generate images based on descriptions.", integration: { - provider: 'brave', - method: 'search', + provider: "dalle", + method: "generate_image", setup: { - api_key: 'your_brave_api_key', + api_key: "your_openai_api_key", }, }, }); - /* - * 💬 Chat 💬 - */ + return agent; +} - // Start an interactive chat session with the agent - const session = await client.sessions.create({ - agentId: agent.id, - contextOverflow: 'adaptive', /* 🧠 Julep will dynamically compute the context window if needed */ - }); +const taskYaml = ` +name: Story and Comic Creator +description: Create a story based on an idea and generate a 4-panel comic strip illustrating the story. + +main: + # Step 1: Generate a story and outline into 4 panels + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the idea '{{_.idea}}', write a short story suitable for a 4-panel comic strip. + Provide the story and a numbered list of 4 brief descriptions for each panel illustrating key moments in the story. + unwrap: true + + # Step 2: Extract the panel descriptions and story + - evaluate: + story: _.split('1. ')[0].trim() + panels: _.match(/\\d+\\.\\s*(.*?)(?=\\d+\\.\\s*|$)/g) + + # Step 3: Generate images for each panel using the image generator tool + - foreach: + in: _.panels + do: + tool: image_generator + arguments: + description: _ + + # Step 4: Generate a catchy title for the story + - prompt: + - role: system + content: You are {{agent.name}}. {{agent.about}} + - role: user + content: > + Based on the story below, generate a catchy title. + + Story: {{outputs[1].story}} + unwrap: true + + # Step 5: Return the story, the generated images, and the title + - return: + title: outputs[3] + story: outputs[1].story + comic_panels: outputs[2].map(output => output.image.url) +`; + +async function createTask(agent: Agent): Promise { + const task = await client.tasks.create(agent.id, yaml.load(taskYaml)); + return task; +} - // 🔄 Chat loop - const readline = require('readline').createInterface({ - input: process.stdin, - output: process.stdout, +async function executeTask(task: Task): Promise { + const execution = await client.executions.create(task.id, { + input: { idea: "A cat who learns to fly" } }); - const askQuestion = (query: string) => new Promise((resolve) => readline.question(query, resolve)); + // 🎉 Watch as the story and comic panels are generated + for await (const transition of client.executions.transitions.stream(execution.id)) { + console.log(transition); + } - while (true) { - const userInput = await askQuestion('You: '); - if (userInput === 'exit') break; + // 📦 Once the execution is finished, retrieve the results + const result = await client.executions.get(execution.id); + return result; +} - const response = await client.sessions.chat(session.id, { - message: userInput, - }); +async function chatWithAgent(agent: Agent): Promise { + const session = await client.sessions.create({ agent_id: agent.id }); - console.log('Agent: ', response.choices[0].message.content); - } - - readline.close(); - - /* - * 📋 Task 📋 - */ - - // Create a recurring research task for the agent - const task = await client.tasks.create(agent.id, { - name: 'Research Task', - description: 'Research the given topic every 24 hours.', - /* 🛠️ Task specific tools */ - tools: [ - { - name: 'send_email', - description: 'Send an email to the user with the results.', - apiCall: { - method: 'post', - url: 'https://api.sendgrid.com/v3/mail/send', - headers: { Authorization: 'Bearer YOUR_SENDGRID_API_KEY' }, - }, - }, - ], - /* 🔢 Task main steps */ - main: [ - // Step 1: Research the topic - { - prompt: "Look up topic '{{_.topic}}' and summarize the results.", - tools: [{ ref: { name: 'web_search' } }], /* 🔍 Use the web search tool from the agent */ - unwrap: true, - }, - // Step 2: Send email with research results - { - tool: 'send_email', - arguments: { - subject: 'Research Results', - body: "'Here are the research results for today: ' + _.content", - to: 'inputs[0].email', // Reference the email from the user's input - }, - }, - // Step 3: Wait for 24 hours before repeating - { sleep: 24 * 60 * 60 }, - ], + // 💬 Send messages to the agent + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout }); - // 🚀 Start the recurring task - await client.executions.create(task.id, { input: { topic: 'TypeScript' } }); + const chat = async () => { + rl.question("Enter a message (or 'quit' to exit): ", async (message) => { + if (message.toLowerCase() === 'quit') { + rl.close(); + return; + } + + const response = await client.sessions.chat(session.id, { message }); + console.log(response); + chat(); + }); + }; + + chat(); +} - /* - * 🔁 This will run the task every 24 hours, - * research for the topic "TypeScript", and - * send the results to the user's email - */ +// Run the example +async function runExample() { + const agent = await createAgent(); + const task = await createTask(agent); + const result = await executeTask(task); + console.log("Task Result:", result); + await chatWithAgent(agent); } -main().catch(console.error); \ No newline at end of file +runExample().catch(console.error); \ No newline at end of file diff --git a/gateway/docker-compose.yml b/gateway/docker-compose.yml index d4ce6a2bd..349973821 100644 --- a/gateway/docker-compose.yml +++ b/gateway/docker-compose.yml @@ -2,11 +2,12 @@ name: julep-gateway services: gateway: - image: julepai/gateway:${TAG} + image: julepai/gateway:${TAG:-dev} environment: - GATEWAY_PORT=80 - JWT_SHARED_KEY=${JWT_SHARED_KEY} - AGENTS_API_URL=${AGENTS_API_URL:-http://agents-api-multi-tenant:8080} + - TEMPORAL_UI_PUBLIC_URL=${TEMPORAL_UI_PUBLIC_URL:-http://temporal-ui-public:8080} - AGENTS_API_KEY=${AGENTS_API_KEY} - AGENTS_API_KEY_HEADER_NAME=${AGENTS_API_KEY_HEADER_NAME:-Authorization} - TRAEFIK_LOG_LEVEL=${TRAEFIK_LOG_LEVEL:-INFO} diff --git a/gateway/entrypoint.sh b/gateway/entrypoint.sh index e28f8db06..65e554592 100755 --- a/gateway/entrypoint.sh +++ b/gateway/entrypoint.sh @@ -4,6 +4,7 @@ AGENTS_API_URL=${AGENTS_API_URL:-http://agents-api:8080} AGENTS_API_KEY_HEADER_NAME=${AGENTS_API_KEY_HEADER_NAME:-Authorization} GATEWAY_PORT=${GATEWAY_PORT:-80} +TEMPORAL_UI_PUBLIC_URL=${TEMPORAL_UI_PUBLIC_URL:-http://temporal-ui-public:8080} for var_name in JWT_SHARED_KEY AGENTS_API_KEY do diff --git a/gateway/traefik.yml.template b/gateway/traefik.yml.template index 4ab3b4367..28ed51de8 100644 --- a/gateway/traefik.yml.template +++ b/gateway/traefik.yml.template @@ -18,6 +18,16 @@ log: http: routers: + agents-api-internal: + entryPoints: + - web + rule: Path(`/api/temporal/{path:.*}`) + middlewares: + - corsHeaders + - agents-api-strip-prefix-api + service: service-agents-api + priority: 2 # Higher priority than the general agents-api route + agents-api: entryPoints: - web @@ -47,6 +57,25 @@ http: service: service-agents-api priority: 3 + temporal-ui-public: + entryPoints: + - web + rule: PathPrefix(`/tasks-ui{path:.*}`) + middlewares: + - corsHeaders + - temporal-ui-strip-prefix + service: service-temporal-ui-public + priority: 1 + + temporal-ui-public-assets: + entryPoints: + - web + rule: PathPrefix(`/_app`) + middlewares: + - corsHeaders + service: service-temporal-ui-public + priority: 2 + middlewares: corsHeaders: headers: @@ -92,6 +121,12 @@ http: JwtHeaders: X-Developer-Id: sub OpaHttpStatusField: allow_status_code + + temporal-ui-strip-prefix: + stripprefix: + prefixes: + - /tasks-ui + forceSlash: false services: service-agents-api: @@ -99,7 +134,13 @@ http: passHostHeader: false servers: - url: $AGENTS_API_URL - + + service-temporal-ui-public: + loadBalancer: + passHostHeader: false + servers: + - url: $TEMPORAL_UI_PUBLIC_URL + experimental: localPlugins: jwt: diff --git a/integrations-service/Dockerfile b/integrations-service/Dockerfile index 784983610..d9a88d876 100644 --- a/integrations-service/Dockerfile +++ b/integrations-service/Dockerfile @@ -5,6 +5,19 @@ FROM python:3.12-slim WORKDIR /app +# Install system dependencies and FFmpeg with all required libraries +RUN apt-get update && apt-get install -y \ + ffmpeg \ + libavcodec-extra \ + libavformat-dev \ + libavutil-dev \ + libswscale-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Verify FFmpeg installation +RUN ffmpeg -version + # Install Poetry RUN pip install poetry @@ -18,5 +31,9 @@ RUN poetry config virtualenvs.create false \ # Copy project COPY . ./ -# Run the application -ENTRYPOINT ["python", "-m", "integrations.web", "--host", "0.0.0.0", "--port", "8000"] +# Set proper signal handling +ENV PYTHONUNBUFFERED=1 +ENV GUNICORN_CMD_ARGS="--capture-output --enable-stdio-inheritance" + +# Run the application with proper signal handling +ENTRYPOINT ["gunicorn", "integrations.web:app", "-c", "gunicorn_conf.py"] \ No newline at end of file diff --git a/integrations-service/docker-compose.yml b/integrations-service/docker-compose.yml index 750dc473d..b8c19957c 100644 --- a/integrations-service/docker-compose.yml +++ b/integrations-service/docker-compose.yml @@ -2,17 +2,29 @@ name: julep-integrations # Shared environment variables x--shared-environment: &shared-environment - OPENAI_API_KEY: ${OPENAI_API_KEY} + INTEGRATIONS_SERVICE_PORT: ${INTEGRATIONS_SERVICE_PORT:-8000} + BROWSERBASE_API_KEY: ${BROWSERBASE_API_KEY} + BROWSERBASE_PROJECT_ID: ${BROWSERBASE_PROJECT_ID} + OPENWEATHER_API_KEY: ${OPENWEATHER_API_KEY} + SPIDER_API_KEY: ${SPIDER_API_KEY} + BRAVE_API_KEY: ${BRAVE_API_KEY} + LLAMA_API_KEY: ${LLAMA_API_KEY} + CLOUDINARY_API_KEY: ${CLOUDINARY_API_KEY} + CLOUDINARY_API_SECRET: ${CLOUDINARY_API_SECRET} + CLOUDINARY_CLOUD_NAME: ${CLOUDINARY_CLOUD_NAME} + MAILGUN_PASSWORD: ${MAILGUN_PASSWORD} services: integrations: + image: julepai/integrations:${TAG:-dev} + build: . + environment: <<: *shared-environment - build: . ports: - - "8000:8000" - + - "${INTEGRATIONS_SERVICE_PORT:-8000}:${INTEGRATIONS_SERVICE_PORT:-8000}" # map host to container port + develop: watch: - action: sync+restart @@ -23,4 +35,4 @@ services: - action: rebuild path: poetry.lock - action: rebuild - path: Dockerfile \ No newline at end of file + path: Dockerfile diff --git a/integrations-service/gunicorn_conf.py b/integrations-service/gunicorn_conf.py new file mode 100644 index 000000000..e7fad22a5 --- /dev/null +++ b/integrations-service/gunicorn_conf.py @@ -0,0 +1,43 @@ +import multiprocessing +import os + +TESTING = os.getenv("TESTING", "false").lower() == "true" +DEBUG = os.getenv("DEBUG", "false").lower() == "true" +AGENTS_API_DEBUG = os.getenv("AGENTS_API_DEBUG", "false").lower() == "true" + +# Gunicorn config variables +workers = ( + (multiprocessing.cpu_count() // 2) + if not (TESTING or AGENTS_API_DEBUG or DEBUG) + else 1 +) +worker_class = "uvicorn.workers.UvicornWorker" +bind = "0.0.0.0:8000" +keepalive = 120 +timeout = 120 +errorlog = "-" +accesslog = "-" +loglevel = "info" +graceful_timeout = 30 +max_requests = 1000 +max_requests_jitter = 50 +preload_app = False + + +def when_ready(server): + """Run when server is ready to handle requests.""" + # Ensure proper permissions for any required directories + for directory in ["logs", "run"]: + path = os.path.join(os.getcwd(), directory) + if not os.path.exists(path): + os.makedirs(path, mode=0o755) + + +def on_starting(server): + """Run when server starts.""" + server.log.setup(server.app.cfg) + + +def worker_exit(server, worker): + """Clean up on worker exit.""" + server.log.info(f"Worker {worker.pid} exiting gracefully") diff --git a/integrations-service/integrations/__main__.py b/integrations-service/integrations/__main__.py new file mode 100644 index 000000000..e420f4b81 --- /dev/null +++ b/integrations-service/integrations/__main__.py @@ -0,0 +1,4 @@ +from .web import main + +if __name__ == "__main__": + main() diff --git a/integrations-service/integrations/autogen/Agents.py b/integrations-service/integrations/autogen/Agents.py new file mode 100644 index 000000000..5dab2c7b2 --- /dev/null +++ b/integrations-service/integrations/autogen/Agents.py @@ -0,0 +1,198 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + +from .Chat import DefaultChatSettings + + +class Agent(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + metadata: dict[str, Any] | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = [] + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ + + +class CreateAgentRequest(BaseModel): + """ + Payload for creating a agent (and associated documents) + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = [] + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ + + +class CreateOrUpdateAgentRequest(CreateAgentRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = [] + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ + + +class PatchAgentRequest(BaseModel): + """ + Payload for patching a agent + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = [] + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ + + +class UpdateAgentRequest(BaseModel): + """ + Payload for updating a agent + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = [] + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ diff --git a/integrations-service/integrations/autogen/Chat.py b/integrations-service/integrations/autogen/Chat.py new file mode 100644 index 000000000..042f9164d --- /dev/null +++ b/integrations-service/integrations/autogen/Chat.py @@ -0,0 +1,684 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool + +from .Common import LogitBias +from .Docs import DocReference +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + CreateToolRequest, + NamedToolChoice, +) + + +class BaseChatOutput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + index: int + finish_reason: Literal["stop", "length", "content_filter", "tool_calls"] = "stop" + """ + The reason the model stopped generating tokens + """ + logprobs: LogProbResponse | None = None + """ + The log probabilities of tokens + """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = None + """ + The tool calls generated by the model + """ + + +class BaseChatResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + usage: CompetionUsage | None = None + """ + Usage statistics for the completion request + """ + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] + """ + Background job IDs that may have been spawned from this interaction. + """ + docs: Annotated[ + list[DocReference], Field(json_schema_extra={"readOnly": True}) + ] = [] + """ + Documents referenced for this request (for citation purposes). + """ + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + + +class BaseTokenLogProb(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + token: str + logprob: float + """ + The log probability of the token + """ + bytes: list[int] | None = None + + +class ChatInputData(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + messages: Annotated[list[Message], Field(min_length=1)] + """ + A list of new input messages comprising the conversation so far. + """ + tools: list[CreateToolRequest] = [] + """ + (Advanced) List of tools that are provided in addition to agent's default set of tools. + """ + tool_choice: Literal["auto", "none"] | NamedToolChoice | None = None + """ + Can be one of existing tools given to the agent earlier or the ones provided in this request. + """ + + +class ChatOutputChunk(BaseChatOutput): + """ + Streaming chat completion output + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + delta: Delta + """ + The message generated by the model + """ + + +class ChunkChatResponse(BaseChatResponse): + model_config = ConfigDict( + populate_by_name=True, + ) + choices: list[ChatOutputChunk] + """ + The deltas generated by the model + """ + + +class CompetionUsage(BaseModel): + """ + Usage statistics for the completion request + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + completion_tokens: Annotated[ + int | None, Field(json_schema_extra={"readOnly": True}) + ] = None + """ + Number of tokens in the generated completion + """ + prompt_tokens: Annotated[ + int | None, Field(json_schema_extra={"readOnly": True}) + ] = None + """ + Number of tokens in the prompt + """ + total_tokens: Annotated[int | None, Field(json_schema_extra={"readOnly": True})] = ( + None + ) + """ + Total number of tokens used in the request (prompt + completion) + """ + + +class Content(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Literal["text"] = "text" + """ + The type (fixed to 'text') + """ + + +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + +class ContentItemModel3(Content): + pass + + +class ContentItemModel4(ContentItemModel): + pass + + +class ContentItemModel5(Content): + pass + + +class ContentItemModel6(ContentItemModel): + pass + + +class ContentModel(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel1(Content): + pass + + +class ContentModel2(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + +class ContentModel3(Content): + pass + + +class ContentModel4(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel3] | list[ContentItemModel4] + + +class ContentModel5(Content): + pass + + +class ContentModel6(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel5] | list[ContentItemModel6] + + +class ContentModel7(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + +class Delta(BaseModel): + """ + The message generated by the model + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal["user", "assistant", "system", "tool"] + """ + The role of the message + """ + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[ContentModel1 | ContentModel7 | ContentModel2] | None, + Field(...), + ] = None + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None + """ + Whether to continue this message or return a new one + """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ + + +class ImageUrl(BaseModel): + """ + The image URL + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: str + """ + Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) + """ + detail: Literal["low", "high", "auto"] = "auto" + """ + The detail level of the image + """ + + +class LogProbResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: Annotated[list[TokenLogProb] | None, Field(...)] + """ + The log probabilities of the tokens + """ + + +class Message(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal["user", "assistant", "system", "tool"] + """ + The role of the message + """ + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[Content | ContentModel7 | ContentModel] | None, + Field(...), + ] = None + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None + """ + Whether to continue this message or return a new one + """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ + + +class MessageChatResponse(BaseChatResponse): + model_config = ConfigDict( + populate_by_name=True, + ) + choices: list[SingleChatOutput | MultipleChatOutput] + """ + The deltas generated by the model + """ + + +class MessageModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal["user", "assistant", "system", "tool"] + """ + The role of the message + """ + tool_call_id: str | None = None + content: Annotated[ + str | list[str] | list[ContentModel3 | ContentModel7 | ContentModel4] | None, + Field(...), + ] = None + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ + created_at: Annotated[ + AwareDatetime | None, Field(json_schema_extra={"readOnly": True}) + ] = None + """ + When this resource was created as UTC date-time + """ + id: Annotated[UUID | None, Field(json_schema_extra={"readOnly": True})] = None + + +class MultipleChatOutput(BaseChatOutput): + """ + The output returned by the model. Note that, depending on the model provider, they might return more than one message. + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + messages: Annotated[ + list[MessageModel], Field(json_schema_extra={"readOnly": True}, min_length=1) + ] + + +class OpenAISettings(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + frequency_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + presence_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + temperature: Annotated[float | None, Field(ge=0.0, le=5.0)] = None + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + top_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + + +class SchemaCompletionResponseFormat(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["json_schema"] = "json_schema" + """ + The format of the response + """ + json_schema: dict[str, Any] + """ + The schema of the response + """ + + +class SimpleCompletionResponseFormat(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["text", "json_object"] = "text" + """ + The format of the response + """ + + +class SingleChatOutput(BaseChatOutput): + """ + The output returned by the model. Note that, depending on the model provider, they might return more than one message. + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + message: MessageModel + + +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str + + +class TokenLogProb(BaseTokenLogProb): + model_config = ConfigDict( + populate_by_name=True, + ) + top_logprobs: Annotated[ + list[BaseTokenLogProb], + Field(json_schema_extra={"readOnly": True}, min_length=1), + ] + """ + The log probabilities of the tokens + """ + + +class ChatInput(ChatInputData): + model_config = ConfigDict( + populate_by_name=True, + ) + remember: Annotated[StrictBool, Field(json_schema_extra={"readOnly": True})] = False + """ + DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) + """ + recall: StrictBool = True + """ + Whether previous memories and docs should be recalled or not + """ + save: StrictBool = True + """ + Whether this interaction should be stored in the session history or not + """ + model: Annotated[ + str | None, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = None + """ + Identifier of the model to be used + """ + stream: StrictBool = False + """ + Indicates if the server should stream the response as it's generated + """ + stop: Annotated[list[str], Field(max_length=4)] = [] + """ + Up to 4 sequences where the API will stop generating further tokens. + """ + seed: Annotated[int | None, Field(ge=-1, le=1000)] = None + """ + If specified, the system will make a best effort to sample deterministically for that particular seed value + """ + max_tokens: Annotated[int | None, Field(ge=1)] = None + """ + The maximum number of tokens to generate in the chat completion + """ + logit_bias: dict[str, LogitBias] | None = None + """ + Modify the likelihood of specified tokens appearing in the completion + """ + response_format: ( + SimpleCompletionResponseFormat | SchemaCompletionResponseFormat | None + ) = None + """ + Response format (set to `json_object` to restrict output to JSON) + """ + agent: UUID | None = None + """ + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + """ + repetition_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + length_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Minimum probability compared to leading token to be considered + """ + frequency_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + presence_penalty: Annotated[float | None, Field(ge=-2.0, le=2.0)] = None + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + temperature: Annotated[float | None, Field(ge=0.0, le=5.0)] = None + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + top_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + + +class DefaultChatSettings(OpenAISettings): + """ + Default settings for the chat session (also used by the agent) + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + repetition_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + length_penalty: Annotated[float | None, Field(ge=0.0, le=2.0)] = None + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Minimum probability compared to leading token to be considered + """ + + +class ChatSettings(DefaultChatSettings): + model_config = ConfigDict( + populate_by_name=True, + ) + model: Annotated[ + str | None, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = None + """ + Identifier of the model to be used + """ + stream: StrictBool = False + """ + Indicates if the server should stream the response as it's generated + """ + stop: Annotated[list[str], Field(max_length=4)] = [] + """ + Up to 4 sequences where the API will stop generating further tokens. + """ + seed: Annotated[int | None, Field(ge=-1, le=1000)] = None + """ + If specified, the system will make a best effort to sample deterministically for that particular seed value + """ + max_tokens: Annotated[int | None, Field(ge=1)] = None + """ + The maximum number of tokens to generate in the chat completion + """ + logit_bias: dict[str, LogitBias] | None = None + """ + Modify the likelihood of specified tokens appearing in the completion + """ + response_format: ( + SimpleCompletionResponseFormat | SchemaCompletionResponseFormat | None + ) = None + """ + Response format (set to `json_object` to restrict output to JSON) + """ + agent: UUID | None = None + """ + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + """ diff --git a/integrations-service/integrations/autogen/Common.py b/integrations-service/integrations/autogen/Common.py new file mode 100644 index 000000000..888865ab7 --- /dev/null +++ b/integrations-service/integrations/autogen/Common.py @@ -0,0 +1,117 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel + + +class JinjaTemplate(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str + """ + A valid jinja template. + """ + + +class Limit(RootModel[int]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[int, Field(ge=1, lt=1000)] + """ + Limit the number of results + """ + + +class LogitBias(RootModel[float]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[float, Field(ge=-100.0, le=100.0)] + + +class Offset(RootModel[int]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[int, Field(ge=0)] + """ + Offset to apply to the results + """ + + +class PyExpression(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str + """ + A simple python expression compatible with SimpleEval. + """ + + +class ResourceCreatedResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + """ + ID of created resource + """ + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] + """ + IDs (if any) of jobs created as part of this request + """ + + +class ResourceDeletedResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + """ + ID of deleted resource + """ + deleted_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was deleted as UTC date-time + """ + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] + """ + IDs (if any) of jobs created as part of this request + """ + + +class ResourceUpdatedResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + """ + ID of updated resource + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + jobs: Annotated[list[UUID], Field(json_schema_extra={"readOnly": True})] = [] + """ + IDs (if any) of jobs created as part of this request + """ + + +class Uuid(RootModel[UUID]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: UUID diff --git a/integrations-service/integrations/autogen/Docs.py b/integrations-service/integrations/autogen/Docs.py new file mode 100644 index 000000000..ffed27c1d --- /dev/null +++ b/integrations-service/integrations/autogen/Docs.py @@ -0,0 +1,208 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + + +class BaseDocSearchRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + limit: Annotated[int, Field(ge=1, le=50)] = 10 + lang: Literal["en-US"] = "en-US" + """ + The language to be used for text-only search. Support for other languages coming soon. + """ + metadata_filter: dict[str, Any] = {} + mmr_strength: Annotated[float, Field(ge=0.0, lt=1.0)] = 0 + """ + MMR Strength (mmr_strength = 1 - mmr_lambda) + """ + + +class CreateDocRequest(BaseModel): + """ + Payload for creating a doc + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + title: Annotated[str, Field(max_length=800)] + """ + Title describing what this document contains + """ + content: str | list[str] + """ + Contents of the document + """ + embed_instruction: str | None = None + """ + Instruction for the embedding model. + """ + + +class Doc(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + metadata: dict[str, Any] | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + title: Annotated[str, Field(max_length=800)] + """ + Title describing what this document contains + """ + content: str | list[str] + """ + Contents of the document + """ + embeddings: Annotated[ + list[float] | list[list[float]] | None, + Field(json_schema_extra={"readOnly": True}), + ] = None + """ + Embeddings for the document + """ + + +class DocOwner(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + role: Literal["user", "agent"] + + +class DocReference(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + owner: DocOwner + """ + The owner of this document. + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + """ + ID of the document + """ + title: str | None = None + snippet: Snippet + distance: float | None = None + + +class DocSearchResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + docs: list[DocReference] + """ + The documents that were found + """ + time: Annotated[float, Field(gt=0.0)] + """ + The time taken to search in seconds + """ + + +class EmbedQueryResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + vectors: list[list[float]] + """ + The embedded vectors + """ + + +class HybridDocSearchRequest(BaseDocSearchRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + confidence: Annotated[float, Field(ge=0.0, le=1.0)] = 0.5 + """ + The confidence cutoff level + """ + alpha: Annotated[float, Field(ge=0.0, le=1.0)] = 0.75 + """ + The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; + """ + text: str + """ + Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. + """ + vector: list[float] + """ + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + """ + + +class MultipleEmbedQueryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: Annotated[list[str], Field(max_length=100, min_length=1)] + """ + Texts to embed + """ + embed_instruction: str = "" + """ + Instruction for the embedding model. + """ + + +class SingleEmbedQueryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + """ + Text to embed + """ + embed_instruction: str = "" + """ + Instruction for the embedding model. + """ + + +class Snippet(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + index: int + content: str + embedding: list[float] | None = None + + +class TextOnlyDocSearchRequest(BaseDocSearchRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + """ + Text to use in the search. + """ + + +class VectorDocSearchRequest(BaseDocSearchRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + confidence: Annotated[float, Field(ge=0.0, le=1.0)] = 0.5 + """ + The confidence cutoff level + """ + vector: list[float] + """ + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + """ diff --git a/integrations-service/integrations/autogen/Entries.py b/integrations-service/integrations/autogen/Entries.py new file mode 100644 index 000000000..de37e77d8 --- /dev/null +++ b/integrations-service/integrations/autogen/Entries.py @@ -0,0 +1,229 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel + +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + Tool, + ToolResponse, +) + + +class BaseEntry(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal["user", "assistant", "system", "tool"] + """ + ChatML role (system|assistant|user|tool) + """ + name: str | None = None + content: ( + list[Content | ContentModel3 | ContentModel] + | Tool + | ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + | str + | ToolResponse + | list[ + list[ContentModel1 | ContentModel3 | ContentModel2] + | Tool + | ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + | str + | ToolResponse + ] + ) + source: Literal[ + "api_request", "api_response", "tool_response", "internal", "summarizer", "meta" + ] + tokenizer: str + token_count: int + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = None + """ + Tool calls generated by the model. + """ + tool_call_id: str | None = None + """ + The tool call id of the tool call this message is a response to + """ + timestamp: Annotated[float, Field(ge=0.0)] + """ + This is the time that this event refers to. + """ + + +class ChatMLRole(RootModel[Literal["user", "assistant", "system", "tool"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal["user", "assistant", "system", "tool"] + """ + ChatML role (system|assistant|user|tool) + """ + + +class Content(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Literal["text"] = "text" + """ + The type (fixed to 'text') + """ + + +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + +class ContentModel(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel1(Content): + pass + + +class ContentModel2(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + +class ContentModel3(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + +class Entry(BaseEntry): + model_config = ConfigDict( + populate_by_name=True, + ) + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + + +class History(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + entries: list[Entry] + relations: list[Relation] + session_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + + +class ImageDetail(RootModel[Literal["low", "high", "auto"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal["low", "high", "auto"] + """ + Image detail level + """ + + +class ImageUrl(BaseModel): + """ + The image URL + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: str + """ + Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) + """ + detail: Literal["low", "high", "auto"] = "auto" + """ + The detail level of the image + """ + + +class Relation(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + head: UUID + relation: str + tail: UUID + + +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str diff --git a/integrations-service/integrations/autogen/Executions.py b/integrations-service/integrations/autogen/Executions.py new file mode 100644 index 000000000..5ccc57e83 --- /dev/null +++ b/integrations-service/integrations/autogen/Executions.py @@ -0,0 +1,188 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + + +class CreateExecutionRequest(BaseModel): + """ + Payload for creating an execution + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + input: dict[str, Any] + """ + The input to the execution + """ + output: Any | None = None + """ + The output of the execution if it succeeded + """ + error: str | None = None + """ + The error of the execution if it failed + """ + metadata: dict[str, Any] | None = None + + +class Execution(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + task_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + """ + The ID of the task that the execution is running + """ + status: Annotated[ + Literal[ + "queued", + "starting", + "running", + "awaiting_input", + "succeeded", + "failed", + "cancelled", + ], + Field(json_schema_extra={"readOnly": True}), + ] + """ + The status of the execution + """ + input: dict[str, Any] + """ + The input to the execution + """ + output: Any | None = None + """ + The output of the execution if it succeeded + """ + error: str | None = None + """ + The error of the execution if it failed + """ + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + metadata: dict[str, Any] | None = None + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + + +class TaskTokenResumeExecutionRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal["running"] = "running" + input: dict[str, Any] | None = None + """ + The input to resume the execution with + """ + + +class TransitionEvent(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal[ + "init", + "init_branch", + "finish", + "finish_branch", + "wait", + "resume", + "error", + "step", + "cancelled", + ], + Field(json_schema_extra={"readOnly": True}), + ] + output: Annotated[Any, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + + +class TransitionTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + workflow: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + For Unicode character safety + See: https://unicode.org/reports/tr31/ + See: https://www.unicode.org/reports/tr39/#Identifier_Characters + """ + step: int + + +class UpdateExecutionRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal[ + "queued", + "starting", + "running", + "awaiting_input", + "succeeded", + "failed", + "cancelled", + ] + + +class ResumeExecutionRequest(UpdateExecutionRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal["running"] = "running" + input: dict[str, Any] | None = None + """ + The input to resume the execution with + """ + + +class StopExecutionRequest(UpdateExecutionRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal["cancelled"] = "cancelled" + reason: str | None = None + """ + The reason for stopping the execution + """ + + +class Transition(TransitionEvent): + model_config = ConfigDict( + populate_by_name=True, + ) + execution_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + current: Annotated[TransitionTarget, Field(json_schema_extra={"readOnly": True})] + next: Annotated[ + TransitionTarget | None, Field(json_schema_extra={"readOnly": True}) + ] + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + metadata: dict[str, Any] | None = None diff --git a/integrations-service/integrations/autogen/Files.py b/integrations-service/integrations/autogen/Files.py new file mode 100644 index 000000000..b7640f8bc --- /dev/null +++ b/integrations-service/integrations/autogen/Files.py @@ -0,0 +1,82 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + + +class CreateFileRequest(BaseModel): + """ + Payload for creating a file + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Name of the file + """ + description: str = "" + """ + Description of the file + """ + mime_type: str | None = None + """ + MIME type of the file + """ + content: str + """ + Base64 encoded content of the file + """ + + +class File(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Name of the file + """ + description: str = "" + """ + Description of the file + """ + mime_type: str | None = None + """ + MIME type of the file + """ + content: str + """ + Base64 encoded content of the file + """ + size: Annotated[int, Field(ge=1, json_schema_extra={"readOnly": True})] + """ + Size of the file in bytes + """ + hash: Annotated[str, Field(json_schema_extra={"readOnly": True})] + """ + Hash of the file + """ diff --git a/integrations-service/integrations/autogen/Jobs.py b/integrations-service/integrations/autogen/Jobs.py new file mode 100644 index 000000000..b17cd9317 --- /dev/null +++ b/integrations-service/integrations/autogen/Jobs.py @@ -0,0 +1,58 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool + + +class JobStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the job + """ + reason: str = "" + """ + Reason for the current state of the job + """ + has_progress: StrictBool = False + """ + Whether this Job supports progress updates + """ + progress: Annotated[float, Field(ge=0.0, le=100.0)] = 0 + """ + Progress percentage + """ + state: Literal[ + "pending", + "in_progress", + "retrying", + "succeeded", + "aborted", + "failed", + "unknown", + ] = "pending" + """ + Current state of the job + """ diff --git a/integrations-service/integrations/autogen/Sessions.py b/integrations-service/integrations/autogen/Sessions.py new file mode 100644 index 000000000..3179c5c93 --- /dev/null +++ b/integrations-service/integrations/autogen/Sessions.py @@ -0,0 +1,260 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool + + +class CreateSessionRequest(BaseModel): + """ + Payload for creating a session + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + user: UUID | None = None + """ + User ID of user associated with this session + """ + users: list[UUID] | None = None + agent: UUID | None = None + """ + Agent ID of agent associated with this session + """ + agents: list[UUID] | None = None + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + render_templates: StrictBool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + auto_run_tools: StrictBool = False + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) + + If a tool call is made, the tool's output will be sent back to the model as the model's input. + If a tool call is not made, the model's output will be returned as is. + """ + metadata: dict[str, Any] | None = None + + +class PatchSessionRequest(BaseModel): + """ + Payload for patching a session + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + render_templates: StrictBool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + auto_run_tools: StrictBool = False + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) + + If a tool call is made, the tool's output will be sent back to the model as the model's input. + If a tool call is not made, the model's output will be returned as is. + """ + metadata: dict[str, Any] | None = None + + +class Session(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + summary: Annotated[str | None, Field(json_schema_extra={"readOnly": True})] = None + """ + Summary (null at the beginning) - generated automatically after every interaction + """ + render_templates: StrictBool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + auto_run_tools: StrictBool = False + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) + + If a tool call is made, the tool's output will be sent back to the model as the model's input. + If a tool call is not made, the model's output will be returned as is. + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + metadata: dict[str, Any] | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + kind: str | None = None + """ + Discriminator property for Session. + """ + + +class SingleAgentMultiUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agent: UUID + users: Annotated[list[UUID], Field(min_length=2)] + + +class SingleAgentNoUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agent: UUID + + +class SingleAgentSingleUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agent: UUID + user: UUID + + +class UpdateSessionRequest(BaseModel): + """ + Payload for updating a session + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + render_templates: StrictBool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + auto_run_tools: StrictBool = False + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) + + If a tool call is made, the tool's output will be sent back to the model as the model's input. + If a tool call is not made, the model's output will be returned as is. + """ + metadata: dict[str, Any] | None = None + + +class CreateOrUpdateSessionRequest(CreateSessionRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + user: UUID | None = None + """ + User ID of user associated with this session + """ + users: list[UUID] | None = None + agent: UUID | None = None + """ + Agent ID of agent associated with this session + """ + agents: list[UUID] | None = None + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.about}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{NEWLINE+NEWLINE}}\n\n{%- if agent.instructions -%}\nInstructions:{{NEWLINE}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{NEWLINE}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{NEWLINE}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{NEWLINE}}\n {%- for tool in tools -%}\n - {{tool.name + NEWLINE}}\n {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%}\n {%- endfor -%}\n{{NEWLINE+NEWLINE}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{NEWLINE}}\n {%- for doc in docs -%}\n {{doc.title}}{{NEWLINE}}\n {%- if doc.content is string -%}\n {{doc.content}}{{NEWLINE}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{NEWLINE}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + render_templates: StrictBool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + auto_run_tools: StrictBool = False + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) + + If a tool call is made, the tool's output will be sent back to the model as the model's input. + If a tool call is not made, the model's output will be returned as is. + """ + metadata: dict[str, Any] | None = None + + +class MultiAgentMultiUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agents: Annotated[list[UUID], Field(min_length=2)] + users: Annotated[list[UUID], Field(min_length=2)] + + +class MultiAgentNoUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agents: Annotated[list[UUID], Field(min_length=2)] + + +class MultiAgentSingleUserSession(Session): + model_config = ConfigDict( + populate_by_name=True, + ) + agents: Annotated[list[UUID], Field(min_length=2)] + user: UUID diff --git a/integrations-service/integrations/autogen/Tasks.py b/integrations-service/integrations/autogen/Tasks.py new file mode 100644 index 000000000..b9212d8cb --- /dev/null +++ b/integrations-service/integrations/autogen/Tasks.py @@ -0,0 +1,1222 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool + +from .Chat import ChatSettings +from .Tools import ( + ChosenBash20241022, + ChosenComputer20241022, + ChosenFunctionCall, + ChosenTextEditor20241022, + CreateToolRequest, + NamedToolChoice, +) + + +class CaseThen(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + case: Literal["_"] | str + """ + The condition to evaluate + """ + then: ( + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + ) + """ + The steps to run if the condition is true + """ + + +class CaseThenUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + case: Literal["_"] | str + """ + The condition to evaluate + """ + then: ( + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + ) + """ + The steps to run if the condition is true + """ + + +class Content(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + """ + A valid jinja template. + """ + type: Literal["text"] = "text" + """ + The type (fixed to 'text') + """ + + +class ContentItem(Content): + pass + + +class ContentItemModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["image"] = "image" + source: Source + + +class ContentItemModel1(Content): + pass + + +class ContentItemModel2(ContentItemModel): + pass + + +class ContentModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + +class ContentModel1(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItem] | list[ContentItemModel] + + +class ContentModel2(Content): + pass + + +class ContentModel3(ContentModel): + pass + + +class ContentModel4(BaseModel): + """ + Anthropic image content part + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + tool_use_id: str + type: Literal["tool_result"] = "tool_result" + content: list[ContentItemModel1] | list[ContentItemModel2] + + +class CreateTaskRequest(BaseModel): + """ + Payload for creating a task + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + description: str = "" + main: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | Main + ], + Field(min_length=1), + ] + """ + The entrypoint of the task. + """ + input_schema: dict[str, Any] | None = None + """ + The schema for the input to the task. `null` means all inputs are valid. + """ + tools: list[TaskTool] = [] + """ + Tools defined specifically for this task not included in the Agent itself. + """ + inherit_tools: StrictBool = False + """ + Whether to inherit tools from the parent agent or not. Defaults to false. + """ + metadata: dict[str, Any] | None = None + + +class ErrorWorkflowStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["error"], Field(json_schema_extra={"readOnly": True})] = ( + "error" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + error: str + """ + The error message + """ + + +class EvaluateStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["evaluate"], Field(json_schema_extra={"readOnly": True}) + ] = "evaluate" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + evaluate: dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + """ + The expression to evaluate + """ + + +class ForeachDo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + in_: Annotated[str, Field(alias="in")] + """ + The variable to iterate over. + VALIDATION: Should NOT return more than 1000 elements. + """ + do: ( + WaitForInputStep + | EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + ) + """ + The steps to run for each iteration + """ + + +class ForeachDoUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + in_: Annotated[str, Field(alias="in")] + """ + The variable to iterate over. + VALIDATION: Should NOT return more than 1000 elements. + """ + do: ( + WaitForInputStep + | EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + ) + """ + The steps to run for each iteration + """ + + +class ForeachStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["foreach"], Field(json_schema_extra={"readOnly": True}) + ] = "foreach" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + foreach: ForeachDo + """ + The steps to run for each iteration + """ + + +class ForeachStepUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + foreach: ForeachDoUpdateItem + """ + The steps to run for each iteration + """ + + +class GetStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["get"], Field(json_schema_extra={"readOnly": True})] = ( + "get" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + get: str + """ + The key to get + """ + + +class IfElseWorkflowStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["if_else"], Field(json_schema_extra={"readOnly": True}) + ] = "if_else" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + if_: Annotated[str, Field(alias="if")] + """ + The condition to evaluate + """ + then: ( + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + ) + """ + The steps to run if the condition is true + """ + else_: Annotated[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | None, + Field(alias="else"), + ] = None + """ + The steps to run if the condition is false + """ + + +class IfElseWorkflowStepUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + if_: Annotated[str, Field(alias="if")] + """ + The condition to evaluate + """ + then: ( + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + ) + """ + The steps to run if the condition is true + """ + else_: Annotated[ + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | None, + Field(alias="else"), + ] = None + """ + The steps to run if the condition is false + """ + + +class ImageUrl(BaseModel): + """ + The image URL + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: str + """ + Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) + """ + detail: Literal["low", "high", "auto"] = "auto" + """ + The detail level of the image + """ + + +class LogStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["log"], Field(json_schema_extra={"readOnly": True})] = ( + "log" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + log: str + """ + The value to log + """ + + +class Main(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["map_reduce"], Field(json_schema_extra={"readOnly": True}) + ] = "map_reduce" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + over: str + """ + The variable to iterate over + """ + map: ( + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + ) + """ + The steps to run for each iteration + """ + reduce: str | None = None + """ + The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named `results` is the accumulator and `_` is the current value. + """ + initial: Any = [] + """ + The initial value of the reduce expression + """ + parallelism: Annotated[int | None, Field(ge=1, le=100)] = None + """ + Whether to run the reduce expression in parallel and how many items to run in each batch + """ + + +class MainModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + over: str + """ + The variable to iterate over + """ + map: ( + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + ) + """ + The steps to run for each iteration + """ + reduce: str | None = None + """ + The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named `results` is the accumulator and `_` is the current value. + """ + initial: Any = [] + """ + The initial value of the reduce expression + """ + parallelism: Annotated[int | None, Field(ge=1, le=100)] = None + """ + Whether to run the reduce expression in parallel and how many items to run in each batch + """ + + +class ParallelStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["parallel"], Field(json_schema_extra={"readOnly": True}) + ] = "parallel" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + parallel: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + ], + Field(max_length=100), + ] + """ + The steps to run in parallel. Max concurrency will depend on the platform. + """ + + +class ParallelStepUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + parallel: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + ], + Field(max_length=100), + ] + """ + The steps to run in parallel. Max concurrency will depend on the platform. + """ + + +class PatchTaskRequest(BaseModel): + """ + Payload for patching a task + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + description: str = "" + main: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStepUpdateItem + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | IfElseWorkflowStepUpdateItem + | SwitchStepUpdateItem + | ForeachStepUpdateItem + | ParallelStepUpdateItem + | MainModel + ] + | None, + Field(min_length=1), + ] = None + """ + The entrypoint of the task. + """ + input_schema: dict[str, Any] | None = None + """ + The schema for the input to the task. `null` means all inputs are valid. + """ + tools: list[TaskTool] = [] + """ + Tools defined specifically for this task not included in the Agent itself. + """ + inherit_tools: StrictBool = False + """ + Whether to inherit tools from the parent agent or not. Defaults to false. + """ + metadata: dict[str, Any] | None = None + + +class PromptItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal["user", "assistant", "system", "tool"] + """ + The role of the message + """ + tool_call_id: str | None = None + content: Annotated[ + list[str] | list[Content | ContentModel | ContentModel1] | str | None, + Field(...), + ] + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(alias="continue")] = None + """ + Whether to continue this message or return a new one + """ + tool_calls: ( + list[ + ChosenFunctionCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 + ] + | None + ) = [] + """ + Tool calls generated by the model. + """ + + +class PromptStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["prompt"], Field(json_schema_extra={"readOnly": True})] = ( + "prompt" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + prompt: list[PromptItem] | str + """ + The prompt to run + """ + tools: Literal["all"] | list[ToolRef | CreateToolRequest] = "all" + """ + The tools to use for the prompt + """ + tool_choice: Literal["auto", "none"] | NamedToolChoice | None = None + """ + The tool choice for the prompt + """ + settings: ChatSettings | None = None + """ + Settings for the prompt + """ + unwrap: StrictBool = False + """ + Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` + """ + auto_run_tools: StrictBool = True + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) + + If a tool call is made, the tool's output will be used as the model's input. + If a tool call is not made, the model's output will be used as the next step's input. + """ + disable_cache: StrictBool = False + """ + Whether to disable caching for the prompt step + """ + + +class PromptStepUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + prompt: list[PromptItem] | str + """ + The prompt to run + """ + tools: Literal["all"] | list[ToolRefUpdateItem | CreateToolRequest] = "all" + """ + The tools to use for the prompt + """ + tool_choice: Literal["auto", "none"] | NamedToolChoice | None = None + """ + The tool choice for the prompt + """ + settings: ChatSettings | None = None + """ + Settings for the prompt + """ + unwrap: StrictBool = False + """ + Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` + """ + auto_run_tools: StrictBool = True + """ + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) + + If a tool call is made, the tool's output will be used as the model's input. + If a tool call is not made, the model's output will be used as the next step's input. + """ + disable_cache: StrictBool = False + """ + Whether to disable caching for the prompt step + """ + + +class ReturnStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["return"], Field(json_schema_extra={"readOnly": True})] = ( + "return" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + return_: Annotated[ + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str], + Field(alias="return"), + ] + """ + The value to return + """ + + +class SetStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["set"], Field(json_schema_extra={"readOnly": True})] = ( + "set" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + set: dict[str, str] + """ + The value to set + """ + + +class SleepFor(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + seconds: Annotated[int, Field(ge=0, le=60)] = 0 + """ + The number of seconds to sleep for + """ + minutes: Annotated[int, Field(ge=0, le=60)] = 0 + """ + The number of minutes to sleep for + """ + hours: Annotated[int, Field(ge=0, le=24)] = 0 + """ + The number of hours to sleep for + """ + days: Annotated[int, Field(ge=0, le=30)] = 0 + """ + The number of days to sleep for + """ + + +class SleepStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["sleep"], Field(json_schema_extra={"readOnly": True})] = ( + "sleep" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + sleep: SleepFor + """ + The duration to sleep for (max 31 days) + """ + + +class Source(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["base64"] = "base64" + media_type: str + data: str + """ + A valid jinja template. + """ + + +class SwitchStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["switch"], Field(json_schema_extra={"readOnly": True})] = ( + "switch" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + switch: Annotated[list[CaseThen], Field(min_length=1)] + """ + The cond tree + """ + + +class SwitchStepUpdateItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + switch: Annotated[list[CaseThenUpdateItem], Field(min_length=1)] + """ + The cond tree + """ + + +class Task(BaseModel): + """ + Object describing a Task + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + description: str = "" + main: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | Main + ], + Field(min_length=1), + ] + """ + The entrypoint of the task. + """ + input_schema: dict[str, Any] | None = None + """ + The schema for the input to the task. `null` means all inputs are valid. + """ + tools: list[TaskTool] = [] + """ + Tools defined specifically for this task not included in the Agent itself. + """ + inherit_tools: StrictBool = False + """ + Whether to inherit tools from the parent agent or not. Defaults to false. + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + metadata: dict[str, Any] | None = None + + +class TaskTool(CreateToolRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + inherited: Annotated[StrictBool, Field(json_schema_extra={"readOnly": True})] = ( + False + ) + """ + Read-only: Whether the tool was inherited or not. Only applies within tasks. + """ + + +class ToolCallStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["tool_call"], Field(json_schema_extra={"readOnly": True}) + ] = "tool_call" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + tool: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + The tool to run + """ + arguments: ( + dict[ + str, + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | list[dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str]] + | str, + ] + | list[ + dict[ + str, + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | list[ + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + ] + | str, + ] + ] + | Literal["_"] + ) = "_" + """ + The input parameters for the tool (defaults to last step output) + """ + + +class ToolRef(BaseModel): + """ + Reference to a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + ref: ToolRefById | ToolRefByName + + +class ToolRefById(BaseModel): + """ + Reference to a tool by id + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + id: str | None = None + + +class ToolRefByName(BaseModel): + """ + Reference to a tool by name + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str | None, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] = None + """ + Valid python identifier names + """ + + +class ToolRefUpdateItem(BaseModel): + """ + Reference to a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + + +class UpdateTaskRequest(BaseModel): + """ + Payload for updating a task + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + description: str = "" + main: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | YieldStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | WaitForInputStep + | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | Main + ], + Field(min_length=1), + ] + """ + The entrypoint of the task. + """ + input_schema: dict[str, Any] | None = None + """ + The schema for the input to the task. `null` means all inputs are valid. + """ + tools: list[TaskTool] = [] + """ + Tools defined specifically for this task not included in the Agent itself. + """ + inherit_tools: StrictBool = False + """ + Whether to inherit tools from the parent agent or not. Defaults to false. + """ + metadata: dict[str, Any] | None = None + + +class WaitForInputInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + info: dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + """ + Any additional info or data + """ + + +class WaitForInputStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["wait_for_input"], Field(json_schema_extra={"readOnly": True}) + ] = "wait_for_input" + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + wait_for_input: WaitForInputInfo + """ + Any additional info or data + """ + + +class YieldStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["yield"], Field(json_schema_extra={"readOnly": True})] = ( + "yield" + ) + """ + The kind of step + """ + label: str | None = None + """ + The label of this step for referencing it from other steps + """ + workflow: str + """ + The subworkflow to run. + VALIDATION: Should resolve to a defined subworkflow. + """ + arguments: ( + dict[str, list[str] | dict[str, str] | list[dict[str, str]] | str] + | Literal["_"] + ) = "_" + """ + The input parameters for the subworkflow (defaults to last step output) + """ diff --git a/integrations-service/integrations/autogen/Tools.py b/integrations-service/integrations/autogen/Tools.py new file mode 100644 index 000000000..5d077f7b6 --- /dev/null +++ b/integrations-service/integrations/autogen/Tools.py @@ -0,0 +1,2638 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any, Literal +from uuid import UUID + +from pydantic import ( + AnyUrl, + AwareDatetime, + BaseModel, + ConfigDict, + Field, + StrictBool, +) + + +class ApiCallDef(BaseModel): + """ + API call definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal[ + "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE" + ] + """ + The HTTP method to use + """ + url: AnyUrl + """ + The URL to call + """ + schema_: Annotated[dict[str, Any] | None, Field(alias="schema")] = None + """ + The schema of the response + """ + headers: dict[str, str] | None = None + """ + The headers to send with the request + """ + content: str | None = None + """ + The content as base64 to send with the request + """ + data: dict[str, Any] | None = None + """ + The data to send as form data + """ + files: dict[str, Any] | None = None + """ + The data to send as files data + """ + json_: Annotated[dict[str, Any] | None, Field(alias="json")] = None + """ + JSON body to send with the request + """ + cookies: dict[str, str] | None = None + """ + Cookies + """ + params: str | dict[str, Any] | None = None + """ + The parameters to send with the request + """ + follow_redirects: StrictBool | None = None + """ + Follow redirects + """ + timeout: int | None = None + """ + The timeout for the request + """ + + +class ApiCallDefUpdate(BaseModel): + """ + API call definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: ( + Literal[ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + ] + | None + ) = None + """ + The HTTP method to use + """ + url: AnyUrl | None = None + """ + The URL to call + """ + schema_: Annotated[dict[str, Any] | None, Field(alias="schema")] = None + """ + The schema of the response + """ + headers: dict[str, str] | None = None + """ + The headers to send with the request + """ + content: str | None = None + """ + The content as base64 to send with the request + """ + data: dict[str, Any] | None = None + """ + The data to send as form data + """ + files: dict[str, Any] | None = None + """ + The data to send as files data + """ + json_: Annotated[dict[str, Any] | None, Field(alias="json")] = None + """ + JSON body to send with the request + """ + cookies: dict[str, str] | None = None + """ + Cookies + """ + params: str | dict[str, Any] | None = None + """ + The parameters to send with the request + """ + follow_redirects: StrictBool | None = None + """ + Follow redirects + """ + timeout: int | None = None + """ + The timeout for the request + """ + + +class ArxivSearchArguments(BaseModel): + """ + Arguments for Arxiv Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str + """ + The search query for searching with Arxiv + """ + id_list: list[str] | None = None + """ + The list of Arxiv IDs to search with + """ + max_results: Annotated[int, Field(ge=1, le=300000)] = 5 + """ + The maximum number of results to return + """ + download_pdf: StrictBool = False + """ + The download the pdf of the results + """ + sort_by: Literal["relevance", "lastUpdatedDate", "submittedDate"] = "relevance" + """ + The sort criterion for the results + """ + sort_order: Literal["ascending", "descending"] = "descending" + """ + The sort order for the results + """ + + +class ArxivSearchArgumentsUpdate(BaseModel): + """ + Arguments for Arxiv Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str | None = None + """ + The search query for searching with Arxiv + """ + id_list: list[str] | None = None + """ + The list of Arxiv IDs to search with + """ + max_results: Annotated[int, Field(ge=1, le=300000)] = 5 + """ + The maximum number of results to return + """ + download_pdf: StrictBool = False + """ + The download the pdf of the results + """ + sort_by: Literal["relevance", "lastUpdatedDate", "submittedDate"] = "relevance" + """ + The sort criterion for the results + """ + sort_order: Literal["ascending", "descending"] = "descending" + """ + The sort order for the results + """ + + +class BaseChosenToolCall(BaseModel): + """ + The response tool value generated by the model + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + """ + function: FunctionCallOption | None = None + integration: Any | None = None + system: Any | None = None + api_call: Any | None = None + computer_20241022: ChosenComputer20241022 | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: ChosenTextEditor20241022 | None = None + bash_20241022: ChosenBash20241022 | None = None + id: Annotated[str | None, Field(json_schema_extra={"readOnly": True})] = None + + +class BaseIntegrationDef(BaseModel): + """ + Integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal[ + "dummy", + "weather", + "wikipedia", + "spider", + "brave", + "browserbase", + "email", + "remote_browser", + "llama_parse", + "ffmpeg", + "cloudinary", + "arxiv", + ] + """ + The provider of the integration + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters the integration accepts + """ + arguments: Any | None = None + """ + The arguments to pre-apply to the integration call + """ + + +class BaseIntegrationDefUpdate(BaseModel): + """ + Integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: ( + Literal[ + "dummy", + "weather", + "wikipedia", + "spider", + "brave", + "browserbase", + "email", + "remote_browser", + "llama_parse", + "ffmpeg", + "cloudinary", + "arxiv", + ] + | None + ) = None + """ + The provider of the integration + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters the integration accepts + """ + arguments: Any | None = None + """ + The arguments to pre-apply to the integration call + """ + + +class Bash20241022Def(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["bash_20241022"] = "bash_20241022" + name: str = "bash" + + +class Bash20241022DefUpdate(Bash20241022Def): + pass + + +class BraveIntegrationDef(BaseIntegrationDef): + """ + Brave integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["brave"] = "brave" + """ + The provider must be "brave" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: BraveSearchSetup | None = None + """ + The setup parameters for Brave + """ + arguments: BraveSearchArguments | None = None + """ + The arguments for Brave Search + """ + + +class BraveIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Brave integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["brave"] = "brave" + """ + The provider must be "brave" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: BraveSearchSetupUpdate | None = None + """ + The setup parameters for Brave + """ + arguments: BraveSearchArgumentsUpdate | None = None + """ + The arguments for Brave Search + """ + + +class BraveSearchArguments(BaseModel): + """ + Arguments for Brave Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str + """ + The search query for searching with Brave + """ + + +class BraveSearchArgumentsUpdate(BaseModel): + """ + Arguments for Brave Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str | None = None + """ + The search query for searching with Brave + """ + + +class BraveSearchSetup(BaseModel): + """ + Integration definition for Brave Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str + """ + The api key for Brave Search + """ + + +class BraveSearchSetupUpdate(BaseModel): + """ + Integration definition for Brave Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str | None = None + """ + The api key for Brave Search + """ + + +class BrowserbaseCompleteSessionArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + status: Literal["REQUEST_RELEASE"] = "REQUEST_RELEASE" + + +class BrowserbaseCompleteSessionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str | None = None + status: Literal["REQUEST_RELEASE"] = "REQUEST_RELEASE" + + +class BrowserbaseContextArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str, Field(alias="projectId")] + """ + The Project ID. Can be found in Settings. + """ + + +class BrowserbaseContextArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str | None, Field(alias="projectId")] = None + """ + The Project ID. Can be found in Settings. + """ + + +class BrowserbaseCreateSessionArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + project_id: Annotated[str | None, Field(alias="projectId")] = None + """ + The Project ID. Can be found in Settings. + """ + extension_id: Annotated[str | None, Field(alias="extensionId")] = None + """ + The installed Extension ID. See Install Extension from GitHub. + """ + browser_settings: Annotated[dict[str, Any], Field(alias="browserSettings")] = {} + """ + Browser settings + """ + timeout: int = 3600 + """ + Duration in seconds after which the session will automatically end. Defaults to the Project's defaultTimeout. + """ + keep_alive: Annotated[StrictBool, Field(alias="keepAlive")] = False + """ + Set to true to keep the session alive even after disconnections. This is available on the Startup plan only. + """ + proxies: StrictBool | list[dict[str, Any]] = False + """ + Proxy configuration. Can be true for default proxy, or an array of proxy configurations. + """ + + +class BrowserbaseExtensionArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + repository_name: Annotated[str, Field(alias="repositoryName")] + """ + The GitHub repository name. + """ + ref: str | None = None + """ + Ref to install from a branch or tag. + """ + + +class BrowserbaseExtensionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + repository_name: Annotated[str | None, Field(alias="repositoryName")] = None + """ + The GitHub repository name. + """ + ref: str | None = None + """ + Ref to install from a branch or tag. + """ + + +class BrowserbaseGetSessionArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + + +class BrowserbaseGetSessionArgumentsUpdate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str | None = None + + +class BrowserbaseGetSessionConnectUrlArguments(BrowserbaseGetSessionArguments): + pass + + +class BrowserbaseGetSessionConnectUrlArgumentsUpdate( + BrowserbaseGetSessionArgumentsUpdate +): + pass + + +class BrowserbaseGetSessionLiveUrlsArguments(BrowserbaseGetSessionArguments): + pass + + +class BrowserbaseGetSessionLiveUrlsArgumentsUpdate( + BrowserbaseGetSessionArgumentsUpdate +): + pass + + +class BrowserbaseListSessionsArguments(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: Literal["RUNNING", "ERROR", "TIMED_OUT", "COMPLETED"] | None = None + """ + The status of the sessions to list (Available options: RUNNING, ERROR, TIMED_OUT, COMPLETED) + """ + + +class BrowserbaseSetup(BaseModel): + """ + The setup parameters for the browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str + """ + API key for the browserbase integration + """ + project_id: str + """ + The project ID. Can be found in Settings. + """ + api_url: str | None = None + """ + The API URL. Defaults to https://www.browserbase.com + """ + connect_url: str | None = None + """ + The connect URL. Defaults to wss://connect.browserbase.com + """ + + +class BrowserbaseSetupUpdate(BaseModel): + """ + The setup parameters for the browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: str | None = None + """ + API key for the browserbase integration + """ + project_id: str | None = None + """ + The project ID. Can be found in Settings. + """ + api_url: str | None = None + """ + The API URL. Defaults to https://www.browserbase.com + """ + connect_url: str | None = None + """ + The connect URL. Defaults to wss://connect.browserbase.com + """ + + +class ChosenBash20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str | None = None + """ + The bash command to run + """ + restart: StrictBool = False + """ + Whether to restart the tool + """ + + +class ChosenComputer20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: Literal[ + "key", + "type", + "cursor_position", + "mouse_move", + "left_click", + "right_click", + "middle_click", + "double_click", + "screenshot", + ] + """ + The action to perform + """ + text: str | None = None + """ + The text to type + """ + coordinate: list[int] | None = None + """ + The (x, y) pixel coordinate to move the cursor to + """ + + +class ChosenFunctionCall(BaseChosenToolCall): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["function"] = "function" + function: FunctionCallOption + """ + The function to call + """ + + +class ChosenTextEditor20241022(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: Literal["str_replace", "insert", "view", "undo_edit"] + """ + The command to run + """ + path: str + """ + The path to the file + """ + file_text: str | None = None + """ + The content of the file to be created + """ + insert_line: int | None = None + """ + The line to insert the new string after + """ + new_str: str | None = None + """ + The new string to insert + """ + old_str: str | None = None + """ + The string in the file to replace + """ + view_range: list[int] | None = None + """ + The line range to view + """ + + +class CloudinaryEditArguments(BaseModel): + """ + Arguments for Cloudinary media edit + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + public_id: str + """ + The file Public ID in Cloudinary + """ + transformation: list[dict[str, Any]] + """ + The transformation to apply to the file + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + + +class CloudinaryEditArgumentsUpdate(BaseModel): + """ + Arguments for Cloudinary media edit + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + public_id: str | None = None + """ + The file Public ID in Cloudinary + """ + transformation: list[dict[str, Any]] | None = None + """ + The transformation to apply to the file + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + + +class CloudinarySetup(BaseModel): + """ + Setup parameters for Cloudinary integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cloudinary_api_key: str + """ + The API key for Cloudinary + """ + cloudinary_api_secret: str + """ + The API secret for Cloudinary + """ + cloudinary_cloud_name: str + """ + The Cloud name for Cloudinary + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Cloudinary API + """ + + +class CloudinarySetupUpdate(BaseModel): + """ + Setup parameters for Cloudinary integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cloudinary_api_key: str | None = None + """ + The API key for Cloudinary + """ + cloudinary_api_secret: str | None = None + """ + The API secret for Cloudinary + """ + cloudinary_cloud_name: str | None = None + """ + The Cloud name for Cloudinary + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Cloudinary API + """ + + +class CloudinaryUploadArguments(BaseModel): + """ + Arguments for Cloudinary media upload + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + file: str + """ + The URL of the file upload + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + public_id: str | None = None + """ + Optional public ID for the uploaded file + """ + upload_params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class CloudinaryUploadArgumentsUpdate(BaseModel): + """ + Arguments for Cloudinary media upload + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + file: str | None = None + """ + The URL of the file upload + """ + return_base64: StrictBool = False + """ + Return base64 encoded file + """ + public_id: str | None = None + """ + Optional public ID for the uploaded file + """ + upload_params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class Computer20241022Def(BaseModel): + """ + Anthropic new tools + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["computer_20241022"] = "computer_20241022" + name: str = "computer" + display_width_px: Annotated[int, Field(ge=600)] = 1024 + """ + The display width in pixels + """ + display_height_px: Annotated[int, Field(ge=400)] = 768 + """ + The display height in pixels + """ + display_number: Annotated[int, Field(ge=1, le=10)] = 1 + """ + The display number to use + """ + + +class Computer20241022DefUpdate(Computer20241022Def): + """ + Anthropic new tools + """ + + +class CreateToolRequest(BaseModel): + """ + Payload for creating a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + + +class DummyIntegrationDef(BaseIntegrationDef): + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["dummy"] = "dummy" + + +class DummyIntegrationDefUpdate(BaseIntegrationDefUpdate): + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["dummy"] = "dummy" + + +class EmailArguments(BaseModel): + """ + Arguments for Email sending + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + to: str + """ + The email address to send the email to + """ + from_: Annotated[str, Field(alias="from")] + """ + The email address to send the email from + """ + subject: str + """ + The subject of the email + """ + body: str + """ + The body of the email + """ + + +class EmailArgumentsUpdate(BaseModel): + """ + Arguments for Email sending + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + to: str | None = None + """ + The email address to send the email to + """ + from_: Annotated[str | None, Field(alias="from")] = None + """ + The email address to send the email from + """ + subject: str | None = None + """ + The subject of the email + """ + body: str | None = None + """ + The body of the email + """ + + +class EmailIntegrationDef(BaseIntegrationDef): + """ + Email integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["email"] = "email" + """ + The provider must be "email" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: EmailSetup | None = None + """ + The setup parameters for Email + """ + arguments: EmailArguments | None = None + """ + The arguments for Email sending + """ + + +class EmailIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Email integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["email"] = "email" + """ + The provider must be "email" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: EmailSetupUpdate | None = None + """ + The setup parameters for Email + """ + arguments: EmailArgumentsUpdate | None = None + """ + The arguments for Email sending + """ + + +class EmailSetup(BaseModel): + """ + Setup parameters for Email integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + host: str + """ + The host of the email server + """ + port: int + """ + The port of the email server + """ + user: str + """ + The username of the email server + """ + password: str + """ + The password of the email server + """ + + +class EmailSetupUpdate(BaseModel): + """ + Setup parameters for Email integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + host: str | None = None + """ + The host of the email server + """ + port: int | None = None + """ + The port of the email server + """ + user: str | None = None + """ + The username of the email server + """ + password: str | None = None + """ + The password of the email server + """ + + +class FfmpegIntegrationDef(BaseIntegrationDef): + """ + Ffmpeg integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["ffmpeg"] = "ffmpeg" + """ + The provider must be "ffmpeg" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Ffmpeg + """ + arguments: FfmpegSearchArguments | None = None + """ + The arguments for Ffmpeg Search + """ + + +class FfmpegIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Ffmpeg integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["ffmpeg"] = "ffmpeg" + """ + The provider must be "ffmpeg" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Ffmpeg + """ + arguments: FfmpegSearchArgumentsUpdate | None = None + """ + The arguments for Ffmpeg Search + """ + + +class FfmpegSearchArguments(BaseModel): + """ + Arguments for Ffmpeg CMD + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cmd: str + """ + The bash command string + """ + file: str | None = None + """ + The base64 string of the file + """ + + +class FfmpegSearchArgumentsUpdate(BaseModel): + """ + Arguments for Ffmpeg CMD + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + cmd: str | None = None + """ + The bash command string + """ + file: str | None = None + """ + The base64 string of the file + """ + + +class FunctionCallOption(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + """ + The name of the function + """ + arguments: str | None = None + """ + The parameters to pass to the function + """ + + +class FunctionDef(BaseModel): + """ + Function definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Any | None = None + """ + DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. + """ + description: Any | None = None + """ + DO NOT USE: This will be overriden by the tool description. Here only for compatibility reasons. + """ + parameters: dict[str, Any] | None = None + """ + The parameters the function accepts + """ + + +class LlamaParseFetchArguments(BaseModel): + """ + Arguments for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + filename: str | None = None + """ + File Name. If not provided, a random name will be generated. + """ + file: str | list[str] + """ + The base64 string of the file, which can be a single string or a list of strings + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + base64: StrictBool = False + """ + The input file is base64 + """ + + +class LlamaParseFetchArgumentsUpdate(BaseModel): + """ + Arguments for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + filename: str | None = None + """ + File Name. If not provided, a random name will be generated. + """ + file: str | list[str] | None = None + """ + The base64 string of the file, which can be a single string or a list of strings + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + base64: StrictBool = False + """ + The input file is base64 + """ + + +class LlamaParseIntegrationDef(BaseIntegrationDef): + """ + LlamaParse integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["llama_parse"] = "llama_parse" + """ + The provider must be "LlamaParseSetup" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: LlamaParseSetup | None = None + """ + The setup parameters for LlamaParse + """ + arguments: LlamaParseFetchArguments | None = None + """ + The arguments for LlamaParse + """ + + +class LlamaParseIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + LlamaParse integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["llama_parse"] = "llama_parse" + """ + The provider must be "LlamaParseSetup" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: LlamaParseSetupUpdate | None = None + """ + The setup parameters for LlamaParse + """ + arguments: LlamaParseFetchArgumentsUpdate | None = None + """ + The arguments for LlamaParse + """ + + +class LlamaParseSetup(BaseModel): + """ + Setup parameters for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + llamaparse_api_key: str + """ + The API key for LlamaParse + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class LlamaParseSetupUpdate(BaseModel): + """ + Setup parameters for LlamaParse integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + llamaparse_api_key: str | None = None + """ + The API key for LlamaParse + """ + params: dict[str, Any] | None = None + """ + Optional upload parameters + """ + + +class NamedToolChoice(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + function: FunctionCallOption | None = None + + +class PatchToolRequest(BaseModel): + """ + Payload for patching a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str | None, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] = None + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: ( + Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + | None + ) = None + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDefUpdate + | BraveIntegrationDefUpdate + | EmailIntegrationDefUpdate + | SpiderIntegrationDefUpdate + | WikipediaIntegrationDefUpdate + | WeatherIntegrationDefUpdate + | BrowserbaseContextIntegrationDefUpdate + | BrowserbaseExtensionIntegrationDefUpdate + | BrowserbaseListSessionsIntegrationDefUpdate + | BrowserbaseCreateSessionIntegrationDefUpdate + | BrowserbaseGetSessionIntegrationDefUpdate + | BrowserbaseCompleteSessionIntegrationDefUpdate + | BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate + | BrowserbaseGetSessionConnectUrlIntegrationDefUpdate + | RemoteBrowserIntegrationDefUpdate + | LlamaParseIntegrationDefUpdate + | FfmpegIntegrationDefUpdate + | CloudinaryUploadIntegrationDefUpdate + | CloudinaryEditIntegrationDefUpdate + | ArxivIntegrationDefUpdate + | None + ) = None + """ + The integration to call + """ + system: SystemDefUpdate | None = None + """ + The system to call + """ + api_call: ApiCallDefUpdate | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022DefUpdate | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022DefUpdate | None = None + bash_20241022: Bash20241022DefUpdate | None = None + + +class RemoteBrowserArguments(BaseModel): + """ + The arguments for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + action: Literal[ + "key", + "type", + "mouse_move", + "left_click", + "left_click_drag", + "right_click", + "middle_click", + "double_click", + "screenshot", + "cursor_position", + "navigate", + "refresh", + ] + """ + The action to perform + """ + text: str | None = None + """ + The text + """ + coordinate: list | None = None + """ + The coordinate to move the mouse to + """ + + +class RemoteBrowserArgumentsUpdate(BaseModel): + """ + The arguments for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + action: ( + Literal[ + "key", + "type", + "mouse_move", + "left_click", + "left_click_drag", + "right_click", + "middle_click", + "double_click", + "screenshot", + "cursor_position", + "navigate", + "refresh", + ] + | None + ) = None + """ + The action to perform + """ + text: str | None = None + """ + The text + """ + coordinate: list | None = None + """ + The coordinate to move the mouse to + """ + + +class RemoteBrowserIntegrationDef(BaseIntegrationDef): + """ + The integration definition for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["remote_browser"] = "remote_browser" + setup: RemoteBrowserSetup + method: Literal["perform_action"] = "perform_action" + arguments: RemoteBrowserArguments | None = None + + +class RemoteBrowserIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + The integration definition for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["remote_browser"] = "remote_browser" + setup: RemoteBrowserSetup | None = None + method: Literal["perform_action"] = "perform_action" + arguments: RemoteBrowserArgumentsUpdate | None = None + + +class RemoteBrowserSetup(BaseModel): + """ + The setup parameters for the remote browser + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + connect_url: str | None = None + """ + The connection URL for the remote browser + """ + width: int | None = None + """ + The width of the browser + """ + height: int | None = None + """ + The height of the browser + """ + + +class SpiderFetchArguments(BaseModel): + """ + Arguments for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: AnyUrl + """ + The URL to fetch data from + """ + mode: Literal["scrape"] = "scrape" + """ + The type of crawler to use + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Spider API + """ + + +class SpiderFetchArgumentsUpdate(BaseModel): + """ + Arguments for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: AnyUrl | None = None + """ + The URL to fetch data from + """ + mode: Literal["scrape"] = "scrape" + """ + The type of crawler to use + """ + params: dict[str, Any] | None = None + """ + Additional parameters for the Spider API + """ + + +class SpiderIntegrationDef(BaseIntegrationDef): + """ + Spider integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["spider"] = "spider" + """ + The provider must be "spider" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: SpiderSetup | None = None + """ + The setup parameters for Spider + """ + arguments: SpiderFetchArguments | None = None + """ + The arguments for Spider + """ + + +class SpiderIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Spider integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["spider"] = "spider" + """ + The provider must be "spider" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: SpiderSetupUpdate | None = None + """ + The setup parameters for Spider + """ + arguments: SpiderFetchArgumentsUpdate | None = None + """ + The arguments for Spider + """ + + +class SpiderSetup(BaseModel): + """ + Setup parameters for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + spider_api_key: str + """ + The API key for Spider + """ + + +class SpiderSetupUpdate(BaseModel): + """ + Setup parameters for Spider integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + spider_api_key: str | None = None + """ + The API key for Spider + """ + + +class SystemDef(BaseModel): + """ + System definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + resource: Literal["agent", "user", "task", "execution", "doc", "session", "job"] + """ + Resource is the name of the resource to use + """ + operation: Literal[ + "create", + "update", + "patch", + "create_or_update", + "embed", + "change_status", + "search", + "chat", + "history", + "delete", + "get", + "list", + ] + """ + Operation is the name of the operation to perform + """ + resource_id: UUID | None = None + """ + Resource id (if applicable) + """ + subresource: Literal["tool", "doc", "execution", "transition"] | None = None + """ + Sub-resource type (if applicable) + """ + arguments: dict[str, Any] | None = None + """ + The arguments to pre-apply to the system call + """ + + +class SystemDefUpdate(BaseModel): + """ + System definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + resource: ( + Literal["agent", "user", "task", "execution", "doc", "session", "job"] | None + ) = None + """ + Resource is the name of the resource to use + """ + operation: ( + Literal[ + "create", + "update", + "patch", + "create_or_update", + "embed", + "change_status", + "search", + "chat", + "history", + "delete", + "get", + "list", + ] + | None + ) = None + """ + Operation is the name of the operation to perform + """ + resource_id: UUID | None = None + """ + Resource id (if applicable) + """ + subresource: Literal["tool", "doc", "execution", "transition"] | None = None + """ + Sub-resource type (if applicable) + """ + arguments: dict[str, Any] | None = None + """ + The arguments to pre-apply to the system call + """ + + +class TextEditor20241022Def(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["text_editor_20241022"] = "text_editor_20241022" + name: str = "str_replace_editor" + + +class TextEditor20241022DefUpdate(TextEditor20241022Def): + pass + + +class Tool(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + + +class ToolResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + output: dict[str, Any] + """ + The output of the tool + """ + + +class UpdateToolRequest(BaseModel): + """ + Payload for updating a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + type: Literal[ + "function", + "integration", + "system", + "api_call", + "computer_20241022", + "text_editor_20241022", + "bash_20241022", + ] + """ + Type of the tool + """ + description: str | None = None + """ + Description of the tool + """ + function: FunctionDef | None = None + """ + The function to call + """ + integration: ( + DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseContextIntegrationDef + | BrowserbaseExtensionIntegrationDef + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryUploadIntegrationDef + | CloudinaryEditIntegrationDef + | ArxivIntegrationDef + | None + ) = None + """ + The integration to call + """ + system: SystemDef | None = None + """ + The system to call + """ + api_call: ApiCallDef | None = None + """ + The API call to make + """ + computer_20241022: Computer20241022Def | None = None + """ + (Alpha) Anthropic new tools + """ + text_editor_20241022: TextEditor20241022Def | None = None + bash_20241022: Bash20241022Def | None = None + + +class WeatherGetArguments(BaseModel): + """ + Arguments for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + location: str + """ + The location for which to fetch weather data + """ + + +class WeatherGetArgumentsUpdate(BaseModel): + """ + Arguments for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + location: str | None = None + """ + The location for which to fetch weather data + """ + + +class WeatherIntegrationDef(BaseIntegrationDef): + """ + Weather integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["weather"] = "weather" + """ + The provider must be "weather" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: WeatherSetup | None = None + """ + The setup parameters for Weather + """ + arguments: WeatherGetArguments | None = None + """ + The arguments for Weather + """ + + +class WeatherIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Weather integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["weather"] = "weather" + """ + The provider must be "weather" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: WeatherSetupUpdate | None = None + """ + The setup parameters for Weather + """ + arguments: WeatherGetArgumentsUpdate | None = None + """ + The arguments for Weather + """ + + +class WeatherSetup(BaseModel): + """ + Integration definition for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + openweathermap_api_key: str + """ + The api key for OpenWeatherMap + """ + + +class WeatherSetupUpdate(BaseModel): + """ + Integration definition for Weather + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + openweathermap_api_key: str | None = None + """ + The api key for OpenWeatherMap + """ + + +class WikipediaIntegrationDef(BaseIntegrationDef): + """ + Wikipedia integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["wikipedia"] = "wikipedia" + """ + The provider must be "wikipedia" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Wikipedia + """ + arguments: WikipediaSearchArguments | None = None + """ + The arguments for Wikipedia Search + """ + + +class WikipediaIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Wikipedia integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["wikipedia"] = "wikipedia" + """ + The provider must be "wikipedia" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Wikipedia + """ + arguments: WikipediaSearchArgumentsUpdate | None = None + """ + The arguments for Wikipedia Search + """ + + +class WikipediaSearchArguments(BaseModel): + """ + Arguments for Wikipedia Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str + """ + The search query string + """ + load_max_docs: Annotated[int, Field(ge=1, le=10)] = 2 + """ + Maximum number of documents to load + """ + + +class WikipediaSearchArgumentsUpdate(BaseModel): + """ + Arguments for Wikipedia Search + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + query: str | None = None + """ + The search query string + """ + load_max_docs: Annotated[int, Field(ge=1, le=10)] = 2 + """ + Maximum number of documents to load + """ + + +class ArxivIntegrationDef(BaseIntegrationDef): + """ + Arxiv integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["arxiv"] = "arxiv" + """ + The provider must be "arxiv" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Arxiv + """ + arguments: ArxivSearchArguments | None = None + """ + The arguments for Arxiv Search + """ + + +class ArxivIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Arxiv integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["arxiv"] = "arxiv" + """ + The provider must be "arxiv" + """ + method: str | None = None + """ + The specific method of the integration to call + """ + setup: Any | None = None + """ + The setup parameters for Arxiv + """ + arguments: ArxivSearchArgumentsUpdate | None = None + """ + The arguments for Arxiv Search + """ + + +class BaseBrowserbaseIntegrationDef(BaseIntegrationDef): + """ + The base definition for a browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["browserbase"] = "browserbase" + setup: BrowserbaseSetup | None = None + method: ( + Literal[ + "get_live_urls", + "list_sessions", + "create_session", + "get_session", + "complete_session", + "get_connect_url", + "install_extension_from_github", + "create_context", + "get_session_downloads", + "get_logs", + "get_recordings", + ] + | None + ) = None + arguments: Any | None = None + + +class BaseBrowserbaseIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + The base definition for a browserbase integration + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["browserbase"] = "browserbase" + setup: BrowserbaseSetupUpdate | None = None + method: ( + Literal[ + "get_live_urls", + "list_sessions", + "create_session", + "get_session", + "complete_session", + "get_connect_url", + "install_extension_from_github", + "create_context", + "get_session_downloads", + "get_logs", + "get_recordings", + ] + | None + ) = None + arguments: Any | None = None + + +class BaseCloudinaryIntegrationDef(BaseIntegrationDef): + """ + Base Cloudinary integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["cloudinary"] = "cloudinary" + setup: CloudinarySetup | None = None + method: Literal["media_upload", "media_edit"] | None = None + + +class BaseCloudinaryIntegrationDefUpdate(BaseIntegrationDefUpdate): + """ + Base Cloudinary integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + provider: Literal["cloudinary"] = "cloudinary" + setup: CloudinarySetupUpdate | None = None + method: Literal["media_upload", "media_edit"] | None = None + + +class BrowserbaseCompleteSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase complete session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["complete_session"] = "complete_session" + arguments: BrowserbaseCompleteSessionArguments | None = None + + +class BrowserbaseCompleteSessionIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase complete session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["complete_session"] = "complete_session" + arguments: BrowserbaseCompleteSessionArgumentsUpdate | None = None + + +class BrowserbaseContextIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase context provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_context"] = "create_context" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseContextArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseContextIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase context provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_context"] = "create_context" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseContextArgumentsUpdate | None = None + """ + The arguments for the method + """ + + +class BrowserbaseCreateSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase create session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_session"] = "create_session" + arguments: BrowserbaseCreateSessionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseCreateSessionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase create session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["create_session"] = "create_session" + arguments: BrowserbaseCreateSessionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseExtensionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase extension provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["install_extension_from_github"] | None = None + """ + The specific method of the integration to call + """ + arguments: BrowserbaseExtensionArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseExtensionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase extension provider + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["install_extension_from_github"] | None = None + """ + The specific method of the integration to call + """ + arguments: BrowserbaseExtensionArgumentsUpdate | None = None + """ + The arguments for the method + """ + + +class BrowserbaseGetSessionConnectUrlIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session connect url integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_connect_url"] = "get_connect_url" + arguments: BrowserbaseGetSessionConnectUrlArguments | None = None + + +class BrowserbaseGetSessionConnectUrlIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase get session connect url integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_connect_url"] = "get_connect_url" + arguments: BrowserbaseGetSessionConnectUrlArgumentsUpdate | None = None + + +class BrowserbaseGetSessionIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_session"] = "get_session" + arguments: BrowserbaseGetSessionArguments | None = None + + +class BrowserbaseGetSessionIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase get session integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_session"] = "get_session" + arguments: BrowserbaseGetSessionArgumentsUpdate | None = None + + +class BrowserbaseGetSessionLiveUrlsIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase get session live urls integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_live_urls"] = "get_live_urls" + arguments: BrowserbaseGetSessionLiveUrlsArguments | None = None + + +class BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate( + BaseBrowserbaseIntegrationDefUpdate +): + """ + browserbase get session live urls integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["get_live_urls"] = "get_live_urls" + arguments: BrowserbaseGetSessionLiveUrlsArgumentsUpdate | None = None + + +class BrowserbaseListSessionsIntegrationDef(BaseBrowserbaseIntegrationDef): + """ + browserbase list sessions integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["list_sessions"] = "list_sessions" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseListSessionsArguments | None = None + """ + The arguments for the method + """ + + +class BrowserbaseListSessionsIntegrationDefUpdate(BaseBrowserbaseIntegrationDefUpdate): + """ + browserbase list sessions integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["list_sessions"] = "list_sessions" + """ + The specific method of the integration to call + """ + arguments: BrowserbaseListSessionsArguments | None = None + """ + The arguments for the method + """ + + +class CloudinaryEditIntegrationDef(BaseCloudinaryIntegrationDef): + """ + Cloudinary edit integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_edit"] = "media_edit" + arguments: CloudinaryEditArguments | None = None + + +class CloudinaryEditIntegrationDefUpdate(BaseCloudinaryIntegrationDefUpdate): + """ + Cloudinary edit integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_edit"] = "media_edit" + arguments: CloudinaryEditArgumentsUpdate | None = None + + +class CloudinaryUploadIntegrationDef(BaseCloudinaryIntegrationDef): + """ + Cloudinary upload integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_upload"] = "media_upload" + arguments: CloudinaryUploadArguments | None = None + + +class CloudinaryUploadIntegrationDefUpdate(BaseCloudinaryIntegrationDefUpdate): + """ + Cloudinary upload integration definition + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + method: Literal["media_upload"] = "media_upload" + arguments: CloudinaryUploadArgumentsUpdate | None = None diff --git a/integrations-service/integrations/autogen/Users.py b/integrations-service/integrations/autogen/Users.py new file mode 100644 index 000000000..720e21846 --- /dev/null +++ b/integrations-service/integrations/autogen/Users.py @@ -0,0 +1,121 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml + +from __future__ import annotations + +from typing import Annotated, Any +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field + + +class CreateUserRequest(BaseModel): + """ + Payload for creating a user (and associated documents) + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the user + """ + about: str = "" + """ + About the user + """ + + +class PatchUserRequest(BaseModel): + """ + Payload for patching a user + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the user + """ + about: str = "" + """ + About the user + """ + + +class UpdateUserRequest(BaseModel): + """ + Payload for updating a user + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the user + """ + about: str = "" + """ + About the user + """ + + +class User(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] + metadata: dict[str, Any] | None = None + created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was created as UTC date-time + """ + updated_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] + """ + When this resource was updated as UTC date-time + """ + name: Annotated[ + str, + Field( + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] = "" + """ + Name of the user + """ + about: str = "" + """ + About the user + """ + + +class CreateOrUpdateUserRequest(CreateUserRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID diff --git a/integrations-service/integrations/autogen/__init__.py b/integrations-service/integrations/autogen/__init__.py new file mode 100644 index 000000000..3d9935180 --- /dev/null +++ b/integrations-service/integrations/autogen/__init__.py @@ -0,0 +1,2 @@ +# generated by datamodel-codegen: +# filename: openapi-1.0.0.yaml diff --git a/integrations-service/integrations/env.py b/integrations-service/integrations/env.py new file mode 100644 index 000000000..1b6d59395 --- /dev/null +++ b/integrations-service/integrations/env.py @@ -0,0 +1,17 @@ +from environs import Env + +# Initialize the Env object for environment variable parsing. +env = Env() +env.read_env() # Read .env file, if it exists + +# Load environment variables +browserbase_api_key = env.str("BROWSERBASE_API_KEY", default=None) +browserbase_project_id = env.str("BROWSERBASE_PROJECT_ID", default=None) +openweather_api_key = env.str("OPENWEATHER_API_KEY", default=None) +spider_api_key = env.str("SPIDER_API_KEY", default=None) +brave_api_key = env.str("BRAVE_API_KEY", default=None) +llama_api_key = env.str("LLAMA_API_KEY", default=None) +cloudinary_api_key = env.str("CLOUDINARY_API_KEY", default=None) +cloudinary_api_secret = env.str("CLOUDINARY_API_SECRET", default=None) +cloudinary_cloud_name = env.str("CLOUDINARY_CLOUD_NAME", default=None) +mailgun_password = env.str("MAILGUN_PASSWORD", default=None) diff --git a/integrations-service/integrations/models/__init__.py b/integrations-service/integrations/models/__init__.py index 902a4a044..e5fe3f218 100644 --- a/integrations-service/integrations/models/__init__.py +++ b/integrations-service/integrations/models/__init__.py @@ -1,19 +1,58 @@ +from .arxiv import ( + ArxivSearchOutput as ArxivSearchOutput, +) +from .arxiv import ( + ArxivSearchResult as ArxivSearchResult, +) +from .base_models import ( + BaseOutput as BaseOutput, +) from .base_models import ( - BaseArguments, - BaseOutput, - BaseProvider, - BaseProviderMethod, - BaseSetup, - ProviderInfo, -) -from .brave import BraveSearchArguments, BraveSearchOutput, BraveSearchSetup + BaseProvider as BaseProvider, +) +from .base_models import ( + BaseProviderMethod as BaseProviderMethod, +) +from .base_models import ( + ProviderInfo as ProviderInfo, +) +from .brave import ( + BraveSearchOutput as BraveSearchOutput, +) +from .brave import ( + SearchResult as SearchResult, +) +from .browserbase import ( + BrowserbaseCompleteSessionOutput as BrowserbaseCompleteSessionOutput, +) from .browserbase import ( - BrowserBaseLoadArguments, - BrowserBaseLoadOutput, - BrowserBaseSetup, -) -from .email import EmailArguments, EmailOutput, EmailSetup -from .hacker_news import HackerNewsFetchArguments, HackerNewsFetchOutput -from .spider import SpiderFetchArguments, SpiderFetchOutput, SpiderSetup -from .weather import WeatherGetArguments, WeatherGetOutput, WeatherSetup -from .wikipedia import WikipediaSearchArguments, WikipediaSearchOutput + BrowserbaseCreateSessionOutput as BrowserbaseCreateSessionOutput, +) +from .browserbase import ( + BrowserbaseExtensionOutput as BrowserbaseExtensionOutput, +) +from .browserbase import ( + BrowserbaseGetSessionConnectUrlOutput as BrowserbaseGetSessionConnectUrlOutput, +) +from .browserbase import ( + BrowserbaseGetSessionLiveUrlsOutput as BrowserbaseGetSessionLiveUrlsOutput, +) +from .browserbase import ( + BrowserbaseGetSessionOutput as BrowserbaseGetSessionOutput, +) +from .browserbase import ( + BrowserbaseListSessionsOutput as BrowserbaseListSessionsOutput, +) +from .cloudinary import ( + CloudinaryEditOutput as CloudinaryEditOutput, +) +from .cloudinary import ( + CloudinaryUploadOutput as CloudinaryUploadOutput, +) +from .email import EmailOutput as EmailOutput +from .ffmpeg import FfmpegSearchOutput as FfmpegSearchOutput +from .llama_parse import LlamaParseFetchOutput as LlamaParseFetchOutput +from .remote_browser import RemoteBrowserOutput as RemoteBrowserOutput +from .spider import SpiderFetchOutput as SpiderFetchOutput +from .weather import WeatherGetOutput as WeatherGetOutput +from .wikipedia import WikipediaSearchOutput as WikipediaSearchOutput diff --git a/integrations-service/integrations/models/arxiv.py b/integrations-service/integrations/models/arxiv.py new file mode 100644 index 000000000..31edf455a --- /dev/null +++ b/integrations-service/integrations/models/arxiv.py @@ -0,0 +1,26 @@ +from typing import List, Optional + +from pydantic import BaseModel, Field + +from .base_models import BaseOutput + + +class ArxivSearchResult(BaseModel): + entry_id: Optional[str] = None + title: Optional[str] = None + updated: Optional[str] = None + published: Optional[str] = None + authors: Optional[List[str]] = None + summary: Optional[str] = None + comment: Optional[str] = None + journal_ref: Optional[str] = None + doi: Optional[str] = None + primary_category: Optional[str] = None + categories: Optional[List[str]] = None + links: Optional[List[str]] = None + pdf_url: Optional[str] = None + pdf_downloaded: Optional[dict] = None + + +class ArxivSearchOutput(BaseOutput): + result: List[ArxivSearchResult] = Field(..., description="A list of search results") diff --git a/integrations-service/integrations/models/base_models.py b/integrations-service/integrations/models/base_models.py index 9f119d460..6d43f67b2 100644 --- a/integrations-service/integrations/models/base_models.py +++ b/integrations-service/integrations/models/base_models.py @@ -1,17 +1,11 @@ -from typing import Annotated, Any, Optional +from typing import Annotated, Optional -from pydantic import BaseModel, Field, RootModel +from pydantic import BaseModel, Field from pydantic_core import Url IdentifierName = Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] -class BaseSetup(BaseModel): ... - - -class BaseArguments(BaseModel): ... - - class BaseOutput(BaseModel): ... @@ -25,12 +19,12 @@ class ProviderInfo(BaseModel): class BaseProviderMethod(BaseModel): method: IdentifierName description: str - arguments: type[BaseArguments] + arguments: type[BaseModel] output: type[BaseOutput] class BaseProvider(BaseModel): provider: IdentifierName - setup: type[BaseSetup] | None + setup: type[BaseModel] | None methods: list[BaseProviderMethod] info: ProviderInfo diff --git a/integrations-service/integrations/models/brave.py b/integrations-service/integrations/models/brave.py index bbf3ca077..dd721d222 100644 --- a/integrations-service/integrations/models/brave.py +++ b/integrations-service/integrations/models/brave.py @@ -1,19 +1,15 @@ -from pydantic import Field +from typing import List -from .base_models import ( - BaseArguments, - BaseOutput, - BaseSetup, -) +from pydantic import BaseModel, Field +from .base_models import BaseOutput -class BraveSearchSetup(BaseSetup): - api_key: str = Field(..., description="The api key for Brave Search") - -class BraveSearchArguments(BaseArguments): - query: str = Field(..., description="The search query for searching with Brave") +class SearchResult(BaseModel): + title: str + link: str + snippet: str class BraveSearchOutput(BaseOutput): - result: str = Field(..., description="The result of the Brave Search") + result: List[SearchResult] = Field(..., description="A list of search results") diff --git a/integrations-service/integrations/models/browserbase.py b/integrations-service/integrations/models/browserbase.py index fdc585090..46f332e57 100644 --- a/integrations-service/integrations/models/browserbase.py +++ b/integrations-service/integrations/models/browserbase.py @@ -1,29 +1,113 @@ -from typing import List, Optional +from typing import Literal, Optional -from langchain_core.documents import Document -from pydantic import Field -from pydantic_core import Url +from browserbase import DebugConnectionURLs, Session +from pydantic import AnyUrl, Field -from .base_models import ( - BaseArguments, - BaseOutput, - BaseSetup, -) +from .base_models import BaseOutput -class BrowserBaseSetup(BaseSetup): - api_key: str = Field(..., description="The api key for BrowserBase") - project_id: str = Field(..., description="The project id for BrowserBase") - session_id: Optional[str] = Field( - None, description="The session id for BrowserBase" +class BrowserbaseListSessionsOutput(BaseOutput): + sessions: list[Session] = Field(..., description="The list of sessions") + + +class BrowserbaseCreateSessionOutput(BaseOutput): + id: str = Field(..., description="Unique identifier for the session") + createdAt: str | None = Field( + None, description="Timestamp indicating when the session was created" + ) + projectId: str | None = Field( + None, description="The Project ID linked to the Session" + ) + startedAt: str | None = Field( + None, description="Timestamp when the session started" + ) + endedAt: str | None = Field(None, description="Timestamp when the session ended") + expiresAt: str | None = Field( + None, description="Timestamp when the session is set to expire" + ) + status: None | Literal["RUNNING", "ERROR", "TIMED_OUT", "COMPLETED"] = Field( + None, description="Current status of the session" + ) + proxyBytes: int | None = Field(None, description="Bytes used via the Proxy") + avgCpuUsage: int | None = Field(None, description="CPU used by the Session") + memoryUsage: int | None = Field(None, description="Memory used by the Session") + keepAlive: bool | None = Field( + None, + description="Indicates if the Session was created to be kept alive upon disconnections", + ) + contextId: str | None = Field( + None, description="Optional. The Context linked to the Session." ) -class BrowserBaseLoadArguments(BaseArguments): - urls: List[Url] = Field(..., description="The urls for loading with BrowserBase") +class BrowserbaseGetSessionOutput(BaseOutput): + id: str = Field(..., description="Unique identifier for the session") + createdAt: str | None = Field( + None, description="Timestamp indicating when the session was created" + ) + projectId: str | None = Field( + None, description="The Project ID linked to the Session" + ) + startedAt: str | None = Field( + None, description="Timestamp when the session started" + ) + endedAt: str | None = Field(None, description="Timestamp when the session ended") + expiresAt: str | None = Field( + None, description="Timestamp when the session is set to expire" + ) + status: None | Literal["RUNNING", "ERROR", "TIMED_OUT", "COMPLETED"] = Field( + None, description="Current status of the session" + ) + proxyBytes: int | None = Field(None, description="Bytes used via the Proxy") + avgCpuUsage: int | None = Field(None, description="CPU used by the Session") + memoryUsage: int | None = Field(None, description="Memory used by the Session") + keepAlive: bool | None = Field( + None, + description="Indicates if the Session was created to be kept alive upon disconnections", + ) + contextId: str | None = Field( + None, description="Optional. The Context linked to the Session." + ) -class BrowserBaseLoadOutput(BaseOutput): - documents: List[Document] = Field( - ..., description="The documents loaded from the urls" +class BrowserbaseCompleteSessionOutput(BaseOutput): + success: bool = Field( + ..., description="Indicates if the session was completed successfully" + ) + + +class BrowserbaseExtensionOutput(BaseOutput): + id: str = Field(..., description="Unique identifier for the extension") + + +class BrowserbaseGetSessionConnectUrlOutput(BaseOutput): + url: AnyUrl = Field(..., description="The connection URL for the session") + + +class PageInfo(BaseOutput): + id: Optional[str] = Field(None, description="Unique identifier for the page") + url: Optional[AnyUrl] = Field(None, description="URL of the page") + faviconUrl: Optional[AnyUrl] = Field(None, description="URL for the page's favicon") + title: Optional[str] = Field(None, description="Title of the page") + debuggerUrl: Optional[AnyUrl] = Field( + None, description="URL to access the debugger for this page" + ) + debuggerFullscreenUrl: Optional[AnyUrl] = Field( + None, description="URL to access the debugger in fullscreen for this page" + ) + + +class BrowserbaseGetSessionLiveUrlsOutput(BaseOutput): + urls: DebugConnectionURLs = Field(..., description="The live URLs for the session") + + +class BrowserbaseContextOutput(BaseOutput): + id: str = Field(..., description="Unique identifier for the context") + uploadUrl: str | None = Field(None, description="The upload URL for the context") + publicKey: str | None = Field(None, description="The public key for the context") + cipherAlgorithm: str | None = Field( + None, description="The cipher algorithm for the context" + ) + initializationVectorSize: int | None = Field( + None, description="The size of the initialization vector" ) diff --git a/integrations-service/integrations/models/cloudinary.py b/integrations-service/integrations/models/cloudinary.py new file mode 100644 index 000000000..4ad59f4bf --- /dev/null +++ b/integrations-service/integrations/models/cloudinary.py @@ -0,0 +1,23 @@ +from typing import Optional + +from pydantic import Field + +from .base_models import BaseOutput + + +class CloudinaryUploadOutput(BaseOutput): + url: str = Field(..., description="The URL of the uploaded file") + public_id: str = Field(..., description="The public ID of the uploaded file") + base64: Optional[str] = Field( + None, description="The base64 encoded file if return_base64 is true" + ) + meta_data: Optional[dict] = Field( + None, description="Additional metadata from the upload response" + ) + + +class CloudinaryEditOutput(BaseOutput): + transformed_url: str = Field(..., description="The transformed URL") + base64: Optional[str] = Field( + None, description="The base64 encoded file if return_base64 is true" + ) diff --git a/integrations-service/integrations/models/dalle_image_generator.py b/integrations-service/integrations/models/dalle_image_generator.py deleted file mode 100644 index 36a3e45b2..000000000 --- a/integrations-service/integrations/models/dalle_image_generator.py +++ /dev/null @@ -1,9 +0,0 @@ -from pydantic import BaseModel, Field - - -class DalleImageGeneratorSetup(BaseModel): - api_key: str = Field(str, description="The API key for DALL-E") - - -class DalleImageGeneratorArguments(BaseModel): - prompt: str = Field(str, description="The image generation prompt") diff --git a/integrations-service/integrations/models/duckduckgo_search.py b/integrations-service/integrations/models/duckduckgo_search.py deleted file mode 100644 index 109b58d22..000000000 --- a/integrations-service/integrations/models/duckduckgo_search.py +++ /dev/null @@ -1,5 +0,0 @@ -from pydantic import BaseModel, Field - - -class DuckDuckGoSearchExecutionArguments(BaseModel): - query: str = Field(..., description="The search query string") diff --git a/integrations-service/integrations/models/email.py b/integrations-service/integrations/models/email.py index cab794a7d..a53b00eff 100644 --- a/integrations-service/integrations/models/email.py +++ b/integrations-service/integrations/models/email.py @@ -1,24 +1,6 @@ -from pydantic import EmailStr, Field +from pydantic import Field -from .base_models import ( - BaseArguments, - BaseOutput, - BaseSetup, -) - - -class EmailSetup(BaseSetup): - host: str = Field(..., description="The host of the email server") - port: int = Field(..., description="The port of the email server") - user: str = Field(..., description="The username of the email server") - password: str = Field(..., description="The password of the email server") - - -class EmailArguments(BaseArguments): - to: EmailStr = Field(..., description="The email address to send the email to") - from_: EmailStr = Field(..., alias="from", description="The email address to send the email from") - subject: str = Field(..., description="The subject of the email") - body: str = Field(..., description="The body of the email") +from .base_models import BaseOutput class EmailOutput(BaseOutput): diff --git a/integrations-service/integrations/models/execution.py b/integrations-service/integrations/models/execution.py index db1ef801f..397782e87 100644 --- a/integrations-service/integrations/models/execution.py +++ b/integrations-service/integrations/models/execution.py @@ -2,44 +2,122 @@ from pydantic import BaseModel -from .brave import BraveSearchArguments, BraveSearchOutput, BraveSearchSetup +from ..autogen.Tools import ( + ArxivSearchArguments, + # Arguments + BraveSearchArguments, + # Setup + BraveSearchSetup, + BrowserbaseCompleteSessionArguments, + BrowserbaseContextArguments, + BrowserbaseCreateSessionArguments, + BrowserbaseExtensionArguments, + BrowserbaseGetSessionArguments, + BrowserbaseGetSessionConnectUrlArguments, + BrowserbaseGetSessionLiveUrlsArguments, + BrowserbaseListSessionsArguments, + BrowserbaseSetup, + CloudinaryEditArguments, + CloudinarySetup, + CloudinaryUploadArguments, + EmailArguments, + EmailSetup, + FfmpegSearchArguments, + LlamaParseFetchArguments, + LlamaParseSetup, + RemoteBrowserArguments, + RemoteBrowserSetup, + SpiderFetchArguments, + SpiderSetup, + WeatherGetArguments, + WeatherSetup, + WikipediaSearchArguments, +) +from .arxiv import ArxivSearchOutput +from .brave import BraveSearchOutput from .browserbase import ( - BrowserBaseLoadArguments, - BrowserBaseLoadOutput, - BrowserBaseSetup, + BrowserbaseCompleteSessionOutput, + BrowserbaseContextOutput, + BrowserbaseCreateSessionOutput, + BrowserbaseExtensionOutput, + BrowserbaseGetSessionConnectUrlOutput, + BrowserbaseGetSessionLiveUrlsOutput, + BrowserbaseGetSessionOutput, + BrowserbaseListSessionsOutput, ) -from .email import EmailArguments, EmailOutput, EmailSetup -from .hacker_news import HackerNewsFetchArguments, HackerNewsFetchOutput -from .spider import SpiderFetchArguments, SpiderFetchOutput, SpiderSetup -from .weather import WeatherGetArguments, WeatherGetOutput, WeatherSetup -from .wikipedia import WikipediaSearchArguments, WikipediaSearchOutput +from .cloudinary import CloudinaryEditOutput, CloudinaryUploadOutput +from .email import EmailOutput +from .ffmpeg import FfmpegSearchOutput +from .llama_parse import LlamaParseFetchOutput +from .remote_browser import RemoteBrowserOutput +from .spider import SpiderFetchOutput +from .weather import WeatherGetOutput +from .wikipedia import WikipediaSearchOutput + + +class ExecutionError(BaseModel): + error: str + """ + The error message of the integration execution + """ + +# Setup configurations ExecutionSetup = Union[ EmailSetup, SpiderSetup, WeatherSetup, BraveSearchSetup, - BrowserBaseSetup, + BrowserbaseSetup, + RemoteBrowserSetup, + LlamaParseSetup, + CloudinarySetup, ] +# Argument configurations ExecutionArguments = Union[ SpiderFetchArguments, WeatherGetArguments, EmailArguments, - HackerNewsFetchArguments, WikipediaSearchArguments, BraveSearchArguments, - BrowserBaseLoadArguments, + BrowserbaseCreateSessionArguments, + BrowserbaseGetSessionArguments, + BrowserbaseGetSessionConnectUrlArguments, + BrowserbaseGetSessionLiveUrlsArguments, + BrowserbaseCompleteSessionArguments, + BrowserbaseContextArguments, + BrowserbaseExtensionArguments, + BrowserbaseListSessionsArguments, + RemoteBrowserArguments, + LlamaParseFetchArguments, + FfmpegSearchArguments, + CloudinaryUploadArguments, + CloudinaryEditArguments, + ArxivSearchArguments, ] ExecutionResponse = Union[ SpiderFetchOutput, WeatherGetOutput, EmailOutput, - HackerNewsFetchOutput, WikipediaSearchOutput, BraveSearchOutput, - BrowserBaseLoadOutput, + BrowserbaseCreateSessionOutput, + BrowserbaseGetSessionOutput, + BrowserbaseGetSessionConnectUrlOutput, + BrowserbaseGetSessionLiveUrlsOutput, + BrowserbaseCompleteSessionOutput, + BrowserbaseContextOutput, + BrowserbaseExtensionOutput, + BrowserbaseListSessionsOutput, + RemoteBrowserOutput, + LlamaParseFetchOutput, + FfmpegSearchOutput, + CloudinaryEditOutput, + CloudinaryUploadOutput, + ExecutionError, + ArxivSearchOutput, ] @@ -48,7 +126,7 @@ class ExecutionRequest(BaseModel): """ The setup parameters the integration accepts (such as API keys) """ - arguments: ExecutionArguments + arguments: Optional[ExecutionArguments] """ The arguments to pass to the integration """ diff --git a/integrations-service/integrations/models/ffmpeg.py b/integrations-service/integrations/models/ffmpeg.py new file mode 100644 index 000000000..ad773228c --- /dev/null +++ b/integrations-service/integrations/models/ffmpeg.py @@ -0,0 +1,15 @@ +from typing import Optional + +from pydantic import Field + +from .base_models import BaseOutput + + +class FfmpegSearchOutput(BaseOutput): + fileoutput: Optional[str] = Field( + None, description="The output file from the Ffmpeg command" + ) + result: bool = Field(..., description="Whether the Ffmpeg command was successful") + mime_type: Optional[str] = Field( + None, description="The MIME type of the output file" + ) diff --git a/integrations-service/integrations/models/hacker_news.py b/integrations-service/integrations/models/hacker_news.py deleted file mode 100644 index 1d0b92a23..000000000 --- a/integrations-service/integrations/models/hacker_news.py +++ /dev/null @@ -1,15 +0,0 @@ -from langchain_core.documents import Document -from pydantic import Field -from pydantic_core import Url - -from .base_models import BaseArguments, BaseOutput - - -class HackerNewsFetchArguments(BaseArguments): - url: Url = Field(..., description="The URL of the Hacker News thread to fetch") - - -class HackerNewsFetchOutput(BaseOutput): - documents: list[Document] = Field( - ..., description="The documents returned from the Hacker News search" - ) diff --git a/integrations-service/integrations/models/llama_parse.py b/integrations-service/integrations/models/llama_parse.py new file mode 100644 index 000000000..759ec949c --- /dev/null +++ b/integrations-service/integrations/models/llama_parse.py @@ -0,0 +1,10 @@ +from llama_index.core.schema import Document +from pydantic import Field + +from .base_models import BaseOutput + + +class LlamaParseFetchOutput(BaseOutput): + documents: list[Document] = Field( + ..., description="The documents returned from the spider" + ) diff --git a/integrations-service/integrations/models/models.py b/integrations-service/integrations/models/models.py deleted file mode 100644 index 84eb1417a..000000000 --- a/integrations-service/integrations/models/models.py +++ /dev/null @@ -1,81 +0,0 @@ -from typing import Literal, Union - -from pydantic import BaseModel - -from .dalle_image_generator import ( - DalleImageGeneratorArguments, - DalleImageGeneratorSetup, -) -from .duckduckgo_search import DuckDuckGoSearchExecutionArguments -from .hacker_news import HackerNewsExecutionArguments -from .weather import WeatherExecutionArguments, WeatherExecutionSetup -from .wikipedia import WikipediaExecutionArguments - -ExecuteIntegrationArguments = Union[ - WikipediaExecutionArguments, - DuckDuckGoSearchExecutionArguments, - DalleImageGeneratorArguments, - WeatherExecutionArguments, - HackerNewsExecutionArguments, -] - -ExecuteIntegrationSetup = Union[ - DalleImageGeneratorSetup, - WeatherExecutionSetup, -] - - -class IntegrationExecutionRequest(BaseModel): - setup: ExecuteIntegrationSetup | None = None - """ - The setup parameters the integration accepts (such as API keys) - """ - arguments: ExecuteIntegrationArguments - """ - The arguments to pass to the integration - """ - - -class IntegrationExecutionResponse(BaseModel): - result: str - """ - The result of the integration execution - """ - - -class IntegrationDef(BaseModel): - provider: ( - Literal[ - "dummy", - "dalle_image_generator", - "duckduckgo_search", - "hacker_news", - "weather", - "wikipedia", - "twitter", - "web_base", - "requests", - "gmail", - "tts_query", - ] - | None - ) = None - """ - The provider of the integration - """ - method: str | None = None - """ - The specific method of the integration to call - """ - description: str | None = None - """ - Optional description of the integration - """ - setup: dict | None = None - """ - The setup parameters the integration accepts - """ - arguments: dict | None = None - """ - The arguments to pre-apply to the integration call - """ diff --git a/integrations-service/integrations/models/remote_browser.py b/integrations-service/integrations/models/remote_browser.py new file mode 100644 index 000000000..f1f585838 --- /dev/null +++ b/integrations-service/integrations/models/remote_browser.py @@ -0,0 +1,12 @@ +from pydantic import Field + +from .base_models import BaseOutput + + +class RemoteBrowserOutput(BaseOutput): + output: str | None = Field(None, description="The output of the action") + error: str | None = Field(None, description="The error of the action") + base64_image: str | None = Field( + None, description="The base64 encoded image of the action" + ) + system: str | None = Field(None, description="The system output of the action") diff --git a/integrations-service/integrations/models/request.py b/integrations-service/integrations/models/request.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/integrations-service/integrations/models/spider.py b/integrations-service/integrations/models/spider.py index d72eba656..989e8411f 100644 --- a/integrations-service/integrations/models/spider.py +++ b/integrations-service/integrations/models/spider.py @@ -1,18 +1,7 @@ from langchain_core.documents import Document from pydantic import Field -from pydantic_core import Url -from .base_models import BaseArguments, BaseOutput, BaseSetup - - -class SpiderSetup(BaseSetup): - spider_api_key: str = Field(..., description="The request for which to fetch data") - - -class SpiderFetchArguments(BaseArguments): - url: Url = Field(..., description="The url for which to fetch data") - mode: str = Field("scrape", description="The type of crawlers") - params: dict | None = Field(None, description="The parameters for the Spider API") +from .base_models import BaseOutput class SpiderFetchOutput(BaseOutput): diff --git a/integrations-service/integrations/models/weather.py b/integrations-service/integrations/models/weather.py index 47cd0d8e3..1204d45b8 100644 --- a/integrations-service/integrations/models/weather.py +++ b/integrations-service/integrations/models/weather.py @@ -1,22 +1,6 @@ from pydantic import Field -from .base_models import ( - BaseArguments, - BaseOutput, - BaseSetup, -) - - -class WeatherSetup(BaseSetup): - openweathermap_api_key: str = Field( - ..., description="The api key for OpenWeatherMap" - ) - - -class WeatherGetArguments(BaseArguments): - location: str = Field( - ..., description="The location for which to fetch weather data" - ) +from .base_models import BaseOutput class WeatherGetOutput(BaseOutput): diff --git a/integrations-service/integrations/models/wikipedia.py b/integrations-service/integrations/models/wikipedia.py index 8c8e4f623..ee5f600f2 100644 --- a/integrations-service/integrations/models/wikipedia.py +++ b/integrations-service/integrations/models/wikipedia.py @@ -1,17 +1,7 @@ -from typing import Literal - from langchain_core.documents import Document from pydantic import Field -from .base_models import ( - BaseArguments, - BaseOutput, -) - - -class WikipediaSearchArguments(BaseArguments): - query: str = Field(..., description="The search query string") - load_max_docs: int = Field(2, description="Maximum number of documents to load") +from .base_models import BaseOutput class WikipediaSearchOutput(BaseOutput): diff --git a/integrations-service/integrations/providers.py b/integrations-service/integrations/providers.py index 41b5bf757..5fb29b75c 100644 --- a/integrations-service/integrations/providers.py +++ b/integrations-service/integrations/providers.py @@ -1,25 +1,55 @@ -from .models import ( - BaseProvider, - BaseProviderMethod, +from .autogen.Tools import ( + ArxivSearchArguments, + # Arguments imports BraveSearchArguments, - BraveSearchOutput, + # Setup imports + # Setup imports BraveSearchSetup, - BrowserBaseLoadArguments, - BrowserBaseLoadOutput, - BrowserBaseSetup, + BrowserbaseCompleteSessionArguments, + BrowserbaseCreateSessionArguments, + BrowserbaseExtensionArguments, + BrowserbaseGetSessionArguments, + BrowserbaseGetSessionConnectUrlArguments, + BrowserbaseGetSessionLiveUrlsArguments, + BrowserbaseListSessionsArguments, + BrowserbaseSetup, + CloudinaryEditArguments, + CloudinarySetup, + CloudinaryUploadArguments, EmailArguments, - EmailOutput, EmailSetup, - HackerNewsFetchArguments, - HackerNewsFetchOutput, - ProviderInfo, + FfmpegSearchArguments, + LlamaParseFetchArguments, + LlamaParseSetup, + RemoteBrowserArguments, + RemoteBrowserSetup, SpiderFetchArguments, - SpiderFetchOutput, SpiderSetup, WeatherGetArguments, - WeatherGetOutput, WeatherSetup, WikipediaSearchArguments, +) +from .models import ( + ArxivSearchOutput, + BaseProvider, + BaseProviderMethod, + BraveSearchOutput, + BrowserbaseCompleteSessionOutput, + BrowserbaseCreateSessionOutput, + BrowserbaseExtensionOutput, + BrowserbaseGetSessionConnectUrlOutput, + BrowserbaseGetSessionLiveUrlsOutput, + BrowserbaseGetSessionOutput, + BrowserbaseListSessionsOutput, + CloudinaryEditOutput, + CloudinaryUploadOutput, + EmailOutput, + FfmpegSearchOutput, + LlamaParseFetchOutput, + ProviderInfo, + RemoteBrowserOutput, + SpiderFetchOutput, + WeatherGetOutput, WikipediaSearchOutput, ) @@ -61,25 +91,6 @@ ), ) -hacker_news = BaseProvider( - provider="hacker_news", - setup=None, - methods=[ - BaseProviderMethod( - method="fetch", - description="Get the top stories from Hacker News", - arguments=HackerNewsFetchArguments, - output=HackerNewsFetchOutput, - ), - ], - info=ProviderInfo( - url="https://news.ycombinator.com/", - docs="https://news.ycombinator.com/newsguidelines.html", - icon="https://news.ycombinator.com/favicon.ico", - friendly_name="Hacker News", - ), -) - spider = BaseProvider( provider="spider", setup=SpiderSetup, @@ -118,22 +129,22 @@ ), ) -browserbase = BaseProvider( - provider="browserbase", - setup=BrowserBaseSetup, +llama_parse = BaseProvider( + provider="llama_parse", + setup=LlamaParseSetup, methods=[ BaseProviderMethod( - method="load", - description="Load documents from the provided urls", - arguments=BrowserBaseLoadArguments, - output=BrowserBaseLoadOutput, + method="parse", + description="Parse and extract the files", + arguments=LlamaParseFetchArguments, + output=LlamaParseFetchOutput, ), ], info=ProviderInfo( - url="https://browserbase.com/", - docs="https://browserbase.com/docs/", - icon="https://browserbase.com/favicon.ico", - friendly_name="BrowserBase", + friendly_name="LlamaParse", + url="https://www.llamaindex.ai/", + docs="https://docs.cloud.llamaindex.ai/llamaparse/getting_started", + icon="https://www.llamaindex.ai/favicon.ico", ), ) @@ -153,12 +164,153 @@ ), ) -providers = { +browserbase = BaseProvider( + provider="browserbase", + setup=BrowserbaseSetup, + methods=[ + BaseProviderMethod( + method="list_sessions", + description="List sessions in Browserbase", + arguments=BrowserbaseListSessionsArguments, + output=BrowserbaseListSessionsOutput, + ), + BaseProviderMethod( + method="create_session", + description="Create a session in Browserbase", + arguments=BrowserbaseCreateSessionArguments, + output=BrowserbaseCreateSessionOutput, + ), + BaseProviderMethod( + method="get_session", + description="Get a session in Browserbase", + arguments=BrowserbaseGetSessionArguments, + output=BrowserbaseGetSessionOutput, + ), + BaseProviderMethod( + method="complete_session", + description="Complete a session in Browserbase", + arguments=BrowserbaseCompleteSessionArguments, + output=BrowserbaseCompleteSessionOutput, + ), + BaseProviderMethod( + method="get_live_urls", + description="Get sessions' live urls in Browserbase", + arguments=BrowserbaseGetSessionLiveUrlsArguments, + output=BrowserbaseGetSessionLiveUrlsOutput, + ), + BaseProviderMethod( + method="install_extension_from_github", + description="Install an extension from GitHub to the browserbase context", + arguments=BrowserbaseExtensionArguments, + output=BrowserbaseExtensionOutput, + ), + BaseProviderMethod( + method="get_connect_url", + description="Get the connection URL for a session", + arguments=BrowserbaseGetSessionConnectUrlArguments, + output=BrowserbaseGetSessionConnectUrlOutput, + ), + ], + info=ProviderInfo( + friendly_name="BrowserBase", + url="https://browserbase.com/", + docs="https://browserbase.com/docs/", + icon="https://browserbase.com/favicon.ico", + ), +) + +remote_browser = BaseProvider( + provider="remote_browser", + setup=RemoteBrowserSetup, + methods=[ + BaseProviderMethod( + method="perform_action", + description="Perform an action in the browser", + arguments=RemoteBrowserArguments, + output=RemoteBrowserOutput, + ), + ], + info=ProviderInfo( + friendly_name="Remote Browser", + url="https://playwright.dev/", + docs="https://playwright.dev/docs/", + icon="https://playwright.dev/favicon.ico", + ), +) + +ffmpeg = BaseProvider( + provider="ffmpeg", + setup=None, + methods=[ + BaseProviderMethod( + method="bash_cmd", + description="Run FFmpeg bash command", + arguments=FfmpegSearchArguments, + output=FfmpegSearchOutput, + ), + ], + info=ProviderInfo( + url="https://ffmpeg.org/", + docs="https://ffmpeg.org/documentation.html", + icon="https://upload.wikimedia.org/wikipedia/commons/5/5f/FFmpeg_Logo_new.svg", + friendly_name="Ffmpeg", + ), +) + +cloudinary = BaseProvider( + provider="cloudinary", + setup=CloudinarySetup, + methods=[ + BaseProviderMethod( + method="media_edit", + description="Edit media in Cloudinary", + arguments=CloudinaryEditArguments, + output=CloudinaryEditOutput, + ), + BaseProviderMethod( + method="media_upload", + description="Upload media to Cloudinary", + arguments=CloudinaryUploadArguments, + output=CloudinaryUploadOutput, + ), + ], + info=ProviderInfo( + url="https://cloudinary.com/", + docs="https://cloudinary.com/documentation/python_quickstart", + icon="https://cloudinary.com/favicon.ico", + friendly_name="Cloudinary", + ), +) + +arxiv = BaseProvider( + provider="arxiv", + setup=None, + methods=[ + BaseProviderMethod( + method="search", + description="Search in Arxiv", + arguments=ArxivSearchArguments, + output=ArxivSearchOutput, + ), + ], + info=ProviderInfo( + url="https://pypi.org/project/arxiv/", + docs="https://info.arxiv.org/help/api/index.html", + icon="https://arxiv.com/favicon.ico", + friendly_name="Arxiv Search", + ), +) + +available_providers: dict[str, BaseProvider] = { "wikipedia": wikipedia, "weather": weather, - "hacker_news": hacker_news, "spider": spider, "brave": brave, - "browserbase": browserbase, "email": email, + "browserbase": browserbase, + "remote_browser": remote_browser, + "llama_parse": llama_parse, + "ffmpeg": ffmpeg, + "cloudinary": cloudinary, + "arxiv": arxiv, } diff --git a/integrations-service/integrations/routers/__init__.py b/integrations-service/integrations/routers/__init__.py index f1be65754..e69de29bb 100644 --- a/integrations-service/integrations/routers/__init__.py +++ b/integrations-service/integrations/routers/__init__.py @@ -1,2 +0,0 @@ -from .execution.router import router as execution_router -from .integrations.router import router as integrations_router diff --git a/integrations-service/integrations/routers/execution/__init__.py b/integrations-service/integrations/routers/execution/__init__.py index ef8e79dda..f659e6cea 100644 --- a/integrations-service/integrations/routers/execution/__init__.py +++ b/integrations-service/integrations/routers/execution/__init__.py @@ -1 +1 @@ -from .execute import execute +from .execute import execute as execute diff --git a/integrations-service/integrations/routers/execution/execute.py b/integrations-service/integrations/routers/execution/execute.py index df4bf913a..83b5fd242 100644 --- a/integrations-service/integrations/routers/execution/execute.py +++ b/integrations-service/integrations/routers/execution/execute.py @@ -7,7 +7,7 @@ @router.post("/execute/{provider}", tags=["execution"]) -async def execute( +async def execute_default( provider: IdentifierName, data: ExecutionRequest, ) -> ExecutionResponse: @@ -20,13 +20,13 @@ async def execute( @router.post("/execute/{provider}/{method}", tags=["execution"]) -def execute( +async def execute( provider: IdentifierName, method: IdentifierName, data: ExecutionRequest, ) -> ExecutionResponse: try: - return execute_integration( + return await execute_integration( provider=provider, arguments=data.arguments, setup=data.setup, method=method ) except ValueError as e: diff --git a/integrations-service/integrations/routers/integrations/__init__.py b/integrations-service/integrations/routers/integrations/__init__.py index 26eac0aaf..88f77f93e 100644 --- a/integrations-service/integrations/routers/integrations/__init__.py +++ b/integrations-service/integrations/routers/integrations/__init__.py @@ -1,3 +1,3 @@ -from .get_integration import get_integration -from .get_integration_tool import get_integration_tool -from .get_integrations import get_integrations +from .get_integration import get_integration as get_integration +from .get_integration_tool import get_integration_tool as get_integration_tool +from .get_integrations import get_integrations as get_integrations diff --git a/integrations-service/integrations/routers/integrations/get_integration.py b/integrations-service/integrations/routers/integrations/get_integration.py index 2a9b34595..f8c4cda4a 100644 --- a/integrations-service/integrations/routers/integrations/get_integration.py +++ b/integrations-service/integrations/routers/integrations/get_integration.py @@ -1,12 +1,17 @@ -from typing import List +from fastapi import HTTPException -from ...providers import providers +from ... import providers as available_providers +from ...models.base_models import BaseProvider from .router import router @router.get("/integrations/{provider}", tags=["integration"]) async def get_integration(provider: str) -> dict: - integration = providers[provider] + integration: BaseProvider | None = getattr(available_providers, provider, None) + + if integration is None: + raise HTTPException(status_code=404, detail="Integration not found") + return { "provider": integration.provider, "setup": integration.setup.model_json_schema() if integration.setup else None, diff --git a/integrations-service/integrations/routers/integrations/get_integration_tool.py b/integrations-service/integrations/routers/integrations/get_integration_tool.py index 42e3c1bc8..c689be322 100644 --- a/integrations-service/integrations/routers/integrations/get_integration_tool.py +++ b/integrations-service/integrations/routers/integrations/get_integration_tool.py @@ -27,18 +27,18 @@ def convert_to_openai_tool( @router.get("/integrations/{provider}/tool", tags=["integration_tool"]) @router.get("/integrations/{provider}/{method}/tool", tags=["integration_tool"]) async def get_integration_tool(provider: str, method: Optional[str] = None): - from ...providers import providers + from ...providers import available_providers - provider: BaseProvider | None = providers.get(provider, None) + provider_obj: BaseProvider | None = available_providers.get(provider, None) - if not provider: + if not provider_obj: raise HTTPException(status_code=404, detail="Integration not found") if method: - for m in provider.methods: + for m in provider_obj.methods: if m.method == method: - return convert_to_openai_tool(provider, m) + return convert_to_openai_tool(provider_obj, m) else: - return convert_to_openai_tool(provider) + return convert_to_openai_tool(provider_obj) raise HTTPException(status_code=404, detail="Integration not found") diff --git a/integrations-service/integrations/routers/integrations/get_integrations.py b/integrations-service/integrations/routers/integrations/get_integrations.py index b3b3530c7..5a90ec69a 100644 --- a/integrations-service/integrations/routers/integrations/get_integrations.py +++ b/integrations-service/integrations/routers/integrations/get_integrations.py @@ -1,6 +1,6 @@ from typing import List -from ...providers import providers +from ...providers import available_providers from .router import router @@ -26,6 +26,6 @@ async def get_integrations() -> List[dict]: "friendly_name": p.info.friendly_name, }, } - for p in providers.values() + for p in available_providers.values() ] return integrations diff --git a/integrations-service/integrations/utils/execute_integration.py b/integrations-service/integrations/utils/execute_integration.py index ab1907885..5fd298344 100644 --- a/integrations-service/integrations/utils/execute_integration.py +++ b/integrations-service/integrations/utils/execute_integration.py @@ -1,42 +1,58 @@ import importlib +from beartype import beartype +from fastapi import HTTPException + +from .. import providers as available_providers from ..models.base_models import BaseProvider, IdentifierName -from ..models.execution import ExecutionArguments, ExecutionResponse, ExecutionSetup -from ..providers import providers +from ..models.execution import ( + ExecutionArguments, + ExecutionError, + ExecutionResponse, + ExecutionSetup, +) +@beartype async def execute_integration( + *, provider: IdentifierName, - arguments: ExecutionArguments, method: IdentifierName | None = None, setup: ExecutionSetup | None = None, + arguments: ExecutionArguments, ) -> ExecutionResponse: - if provider not in providers: - raise ValueError(f"Unknown provider: {provider}") - provider: BaseProvider = providers[provider] - if method is None: - method = provider.methods[0].method - if method not in [method.method for method in provider.methods]: - raise ValueError(f"Unknown method: {method} for provider: {provider}") + provider_obj = getattr(available_providers, provider, None) + if not provider_obj or not isinstance(provider_obj, BaseProvider): + raise HTTPException(status_code=400, detail=f"Unknown provider: {provider}") + + method = method or provider_obj.methods[0].method + method_config = next((m for m in provider_obj.methods if m.method == method), None) + if not method_config: + raise HTTPException( + status_code=400, detail=f"Unknown method: {method} for provider: {provider}" + ) provider_module = importlib.import_module( - f"integrations.utils.integrations.{provider.provider}", package="integrations" + f"integrations.utils.integrations.{provider_obj.provider}", + package="integrations", ) - execution_function = getattr(provider_module, method) - - if setup: - setup_class = provider.setup - if setup_class and not isinstance(setup, setup_class): - setup = setup_class(**setup.model_dump()) - - arguments_class = next(m for m in provider.methods if m.method == method).arguments - - if not isinstance(arguments, arguments_class): - parsed_arguments = arguments_class(**arguments.model_dump()) - else: - parsed_arguments = arguments - - if setup: - return await execution_function(setup=setup, arguments=parsed_arguments) - else: - return execution_function(arguments=parsed_arguments) + + if ( + setup is not None + and provider_obj.setup + and not isinstance(setup, provider_obj.setup) + ): + setup = provider_obj.setup(**setup.model_dump()) + + arguments = ( + method_config.arguments(**arguments.model_dump()) + if not isinstance(arguments, method_config.arguments) + else arguments + ) + + try: + return await getattr(provider_module, method)( + **({"setup": setup} if setup else {}), arguments=arguments + ) + except BaseException as e: + return ExecutionError(error=str(e)) diff --git a/integrations-service/integrations/utils/integrations/__init__.py b/integrations-service/integrations/utils/integrations/__init__.py index 238dacfd3..e69de29bb 100644 --- a/integrations-service/integrations/utils/integrations/__init__.py +++ b/integrations-service/integrations/utils/integrations/__init__.py @@ -1,7 +0,0 @@ -from .brave import search -from .browserbase import load -from .email import send -from .hacker_news import fetch -from .spider import crawl -from .weather import get -from .wikipedia import search diff --git a/integrations-service/integrations/utils/integrations/__tts_query.py b/integrations-service/integrations/utils/integrations/__tts_query.py deleted file mode 100644 index b4ce59d4d..000000000 --- a/integrations-service/integrations/utils/integrations/__tts_query.py +++ /dev/null @@ -1,20 +0,0 @@ -import os - -from langchain_community.tools import ElevenLabsText2SpeechTool - - -async def tts_query(arguments: dict) -> str: - """ - Converts text to speech using ElevenLabs API and plays the generated audio. - """ - text_to_speak = arguments.get("query") - if not text_to_speak: - raise ValueError("Query parameter is required for text to speech") - - eleven_api_key = os.getenv("ELEVEN_API_KEY") - - tts = ElevenLabsText2SpeechTool(eleven_api_key=eleven_api_key) - - speech_file = tts.run(text_to_speak) - - return tts.play(speech_file) diff --git a/integrations-service/integrations/utils/integrations/__twitter.py b/integrations-service/integrations/utils/integrations/__twitter.py deleted file mode 100644 index b21b18197..000000000 --- a/integrations-service/integrations/utils/integrations/__twitter.py +++ /dev/null @@ -1,32 +0,0 @@ -import os - -from langchain_community.document_loaders import TwitterTweetLoader - - -async def twitter(arguments: dict) -> str: - """ - Loads tweets from specified Twitter users and returns them as formatted string. - """ - bearer_token = os.getenv("TWITTER_BEARER_TOKEN") - if not bearer_token: - raise ValueError("Twitter API bearer token is not set") - - twitter_users = arguments.get("twitter_users") - if not twitter_users: - raise ValueError("Twitter users parameter is required for Twitter loader") - - number_tweets = arguments.get("number_tweets", 50) - - loader = TwitterTweetLoader.from_bearer_token( - oauth2_bearer_token=bearer_token, - twitter_users=twitter_users, - number_tweets=number_tweets, - ) - - documents = loader.load() - - # Format the results as a string - result = "\n\n".join( - [f"Tweet: {doc.page_content}\nMetadata: {doc.metadata}" for doc in documents] - ) - return result diff --git a/integrations-service/integrations/utils/integrations/arxiv.py b/integrations-service/integrations/utils/integrations/arxiv.py new file mode 100644 index 000000000..70b3c14df --- /dev/null +++ b/integrations-service/integrations/utils/integrations/arxiv.py @@ -0,0 +1,89 @@ +import base64 +import tempfile + +import arxiv +from beartype import beartype +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import ArxivSearchArguments +from ...models import ArxivSearchOutput, ArxivSearchResult + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def search(arguments: ArxivSearchArguments) -> ArxivSearchOutput: + """ + Searches Arxiv with the provided query. + + Args: + arguments (ArxivSearchArguments): The search parameters including query, + maximum results, sorting criteria, and + download options. + + Returns: + ArxivSearchOutput: The search results wrapped in an output object. + """ + assert isinstance(arguments, ArxivSearchArguments), "Invalid arguments" + + client = arxiv.Client() + sort_criterion = { + "submittedDate": arxiv.SortCriterion.SubmittedDate, + "lastUpdatedDate": arxiv.SortCriterion.LastUpdatedDate, + }.get(arguments.sort_by, arxiv.SortCriterion.Relevance) + + sort_order = ( + arxiv.SortOrder.Descending + if arguments.sort_order == "descending" + else arxiv.SortOrder.Ascending + ) + + search = arxiv.Search( + query=arguments.query, + max_results=arguments.max_results, + sort_by=sort_criterion, + sort_order=sort_order, + ) + + results = [] + search_results = client.results(search) + + def create_arxiv_search_result(result, pdf_content=None): + return ArxivSearchResult( + entry_id=result.entry_id, + title=result.title, + authors=[author.name for author in result.authors], + summary=result.summary.replace("\n", ""), + published=result.published.strftime("%Y-%m-%d %H:%M:%S"), + updated=result.updated.strftime("%Y-%m-%d %H:%M:%S"), + comment=result.comment, + journal_ref=result.journal_ref, + doi=result.doi, + primary_category=result.primary_category, + categories=result.categories, + links=[link.href for link in result.links], + pdf_url=result.pdf_url, + pdf_downloaded={ + "base64": pdf_content, + "mime_type": "application/pdf", + "title": result.title, + } + if pdf_content + else None, + ) + + if arguments.download_pdf: + for result in search_results: + with tempfile.TemporaryDirectory() as temp_dir: + result.download_pdf(dirpath=temp_dir, filename=f"{result.title}.pdf") + with open(f"{temp_dir}/{result.title}.pdf", "rb") as pdf_file: + pdf_content = base64.b64encode(pdf_file.read()).decode("utf-8") + results.append(create_arxiv_search_result(result, pdf_content)) + else: + for result in search_results: + results.append(create_arxiv_search_result(result)) + + return ArxivSearchOutput(result=results) diff --git a/integrations-service/integrations/utils/integrations/assets/cursor-small.png b/integrations-service/integrations/utils/integrations/assets/cursor-small.png new file mode 100644 index 000000000..b9c59d0a7 Binary files /dev/null and b/integrations-service/integrations/utils/integrations/assets/cursor-small.png differ diff --git a/integrations-service/integrations/utils/integrations/assets/cursor.png b/integrations-service/integrations/utils/integrations/assets/cursor.png new file mode 100755 index 000000000..f8b688afb Binary files /dev/null and b/integrations-service/integrations/utils/integrations/assets/cursor.png differ diff --git a/integrations-service/integrations/utils/integrations/brave.py b/integrations-service/integrations/utils/integrations/brave.py index 10bfe8084..7414e081a 100644 --- a/integrations-service/integrations/utils/integrations/brave.py +++ b/integrations-service/integrations/utils/integrations/brave.py @@ -1,8 +1,20 @@ +import json + +from beartype import beartype from langchain_community.tools import BraveSearch +from tenacity import retry, stop_after_attempt, wait_exponential -from ...models import BraveSearchArguments, BraveSearchOutput, BraveSearchSetup +from ...autogen.Tools import BraveSearchArguments, BraveSearchSetup +from ...env import brave_api_key # Import env to access environment variables +from ...models import BraveSearchOutput, SearchResult +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) async def search( setup: BraveSearchSetup, arguments: BraveSearchArguments ) -> BraveSearchOutput: @@ -13,7 +25,17 @@ async def search( assert isinstance(setup, BraveSearchSetup), "Invalid setup" assert isinstance(arguments, BraveSearchArguments), "Invalid arguments" + # Check if the setup.api_key is 'DEMO_API_KEY' and load from environment if true + if setup.api_key == "DEMO_API_KEY": + setup.api_key = brave_api_key + tool = BraveSearch.from_api_key(api_key=setup.api_key, search_kwargs={"count": 3}) result = tool.run(arguments.query) - return BraveSearchOutput(result=result) + + try: + parsed_result = [SearchResult(**item) for item in json.loads(result)] + except json.JSONDecodeError as e: + raise ValueError("Malformed JSON response from Brave Search") from e + + return BraveSearchOutput(result=parsed_result) diff --git a/integrations-service/integrations/utils/integrations/browserbase.py b/integrations-service/integrations/utils/integrations/browserbase.py index 7cc672662..bd65b7df0 100644 --- a/integrations-service/integrations/utils/integrations/browserbase.py +++ b/integrations-service/integrations/utils/integrations/browserbase.py @@ -1,27 +1,221 @@ -from langchain_community.document_loaders import BrowserbaseLoader +import os +import tempfile -from ...models import BrowserBaseLoadArguments, BrowserBaseLoadOutput, BrowserBaseSetup +import httpx +from beartype import beartype +from browserbase import ( + Browserbase, + BrowserSettings, + CreateSessionOptions, + DebugConnectionURLs, + Session, +) +from tenacity import retry, stop_after_attempt, wait_exponential +from ...autogen.Tools import ( + BrowserbaseCompleteSessionArguments, + BrowserbaseCreateSessionArguments, + BrowserbaseExtensionArguments, + BrowserbaseGetSessionArguments, + BrowserbaseGetSessionConnectUrlArguments, + BrowserbaseGetSessionLiveUrlsArguments, + BrowserbaseListSessionsArguments, + BrowserbaseSetup, +) +from ...env import ( + browserbase_api_key, + browserbase_project_id, +) # Import env to access environment variables +from ...models import ( + BrowserbaseCompleteSessionOutput, + BrowserbaseCreateSessionOutput, + BrowserbaseGetSessionConnectUrlOutput, + BrowserbaseGetSessionLiveUrlsOutput, + BrowserbaseGetSessionOutput, + BrowserbaseListSessionsOutput, +) +from ...models.browserbase import BrowserbaseExtensionOutput -async def load( - setup: BrowserBaseSetup, arguments: BrowserBaseLoadArguments -) -> BrowserBaseLoadOutput: - """ - Loads documents from the provided urls using BrowserBase. - """ - assert isinstance(setup, BrowserBaseSetup), "Invalid setup" - assert isinstance(arguments, BrowserBaseLoadArguments), "Invalid arguments" - - urls = [str(url) for url in arguments.urls] +def get_browserbase_client(setup: BrowserbaseSetup) -> Browserbase: + setup.api_key = ( + browserbase_api_key if setup.api_key == "DEMO_API_KEY" else setup.api_key + ) + setup.project_id = ( + browserbase_project_id + if setup.project_id == "DEMO_PROJECT_ID" + else setup.project_id + ) - loader = BrowserbaseLoader( + return Browserbase( api_key=setup.api_key, project_id=setup.project_id, - session_id=setup.session_id, - urls=urls, - text_content=False, + api_url=setup.api_url, + connect_url=setup.connect_url, ) - documents = loader.load() - return BrowserBaseLoadOutput(documents=documents) + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def list_sessions( + setup: BrowserbaseSetup, arguments: BrowserbaseListSessionsArguments +) -> BrowserbaseListSessionsOutput: + client = get_browserbase_client(setup) + + # FIXME: Implement status filter + # Run the list_sessions method + sessions: list[Session] = client.list_sessions() + + return BrowserbaseListSessionsOutput(sessions=sessions) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def create_session( + setup: BrowserbaseSetup, arguments: BrowserbaseCreateSessionArguments +) -> BrowserbaseCreateSessionOutput: + client = get_browserbase_client(setup) + + if arguments.project_id == "DEMO_PROJECT_ID": + arguments.project_id = browserbase_project_id + + if arguments.project_id == "DEMO_PROJECT_ID": + arguments.project_id = browserbase_project_id + + options = CreateSessionOptions( + projectId=arguments.project_id or setup.project_id, + extensionId=arguments.extension_id, + browserSettings=BrowserSettings(**arguments.browser_settings), + ) + + session = client.create_session(options) + + return BrowserbaseCreateSessionOutput(**session.model_dump()) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def get_session( + setup: BrowserbaseSetup, arguments: BrowserbaseGetSessionArguments +) -> BrowserbaseGetSessionOutput: + client = get_browserbase_client(setup) + + session = client.get_session(arguments.id) + + return BrowserbaseGetSessionOutput(**session.model_dump()) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def complete_session( + setup: BrowserbaseSetup, arguments: BrowserbaseCompleteSessionArguments +) -> BrowserbaseCompleteSessionOutput: + client = get_browserbase_client(setup) + + try: + client.complete_session(arguments.id) + except Exception: + return BrowserbaseCompleteSessionOutput(success=False) + + return BrowserbaseCompleteSessionOutput(success=True) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def get_live_urls( + setup: BrowserbaseSetup, arguments: BrowserbaseGetSessionLiveUrlsArguments +) -> BrowserbaseGetSessionLiveUrlsOutput: + """Get the live URLs for a session.""" + client = get_browserbase_client(setup) + urls: DebugConnectionURLs = client.get_debug_connection_urls(arguments.id) + return BrowserbaseGetSessionLiveUrlsOutput(urls=urls) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def get_connect_url( + setup: BrowserbaseSetup, arguments: BrowserbaseGetSessionConnectUrlArguments +) -> BrowserbaseGetSessionConnectUrlOutput: + client = get_browserbase_client(setup) + + url = client.get_connect_url(arguments.id) + + return BrowserbaseGetSessionConnectUrlOutput(url=url) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def install_extension_from_github( + setup: BrowserbaseSetup, arguments: BrowserbaseExtensionArguments +) -> BrowserbaseExtensionOutput: + """Download and install an extension from GitHub to the user's Browserbase account.""" + + github_url = f"https://github.com/{arguments.repository_name}/archive/refs/tags/{ + arguments.ref}.zip" + + async with httpx.AsyncClient(timeout=600) as client: + # Download the extension zip + response = await client.get(github_url, follow_redirects=True) + response.raise_for_status() + + with tempfile.NamedTemporaryFile( + delete=True, delete_on_close=False, suffix=".zip" + ) as tmp_file: + tmp_file.write(response.content) + tmp_file_path = tmp_file.name + + # Upload the extension to Browserbase + upload_url = "https://www.browserbase.com/v1/extensions" + headers = { + # NOTE: httpx won't add a boundary if Content-Type header is set when you pass files= + # "Content-Type": "multipart/form-data", + "X-BB-API-Key": setup.api_key, + } + + with open(tmp_file_path, "rb") as f: + files = {"file": f} + upload_response = await client.post( + upload_url, headers=headers, files=files + ) + + try: + upload_response.raise_for_status() + except httpx.HTTPStatusError: + print(upload_response.text) + raise + + # Delete the temporary file + try: + os.remove(tmp_file_path) + except FileNotFoundError: + pass + + return BrowserbaseExtensionOutput(id=upload_response.json()["id"]) diff --git a/integrations-service/integrations/utils/integrations/cloudinary.py b/integrations-service/integrations/utils/integrations/cloudinary.py new file mode 100644 index 000000000..ccfecc7cf --- /dev/null +++ b/integrations-service/integrations/utils/integrations/cloudinary.py @@ -0,0 +1,150 @@ +import base64 +import uuid + +import aiohttp +import cloudinary +import cloudinary.uploader +from beartype import beartype +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import ( + CloudinaryEditArguments, + CloudinarySetup, + CloudinaryUploadArguments, +) +from ...env import ( # Import env to access environment variables + cloudinary_api_key, + cloudinary_api_secret, + cloudinary_cloud_name, +) +from ...models.cloudinary import CloudinaryEditOutput, CloudinaryUploadOutput + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def media_upload( + setup: CloudinarySetup, arguments: CloudinaryUploadArguments +) -> CloudinaryUploadOutput: + """ + Upload media to Cloudinary. + """ + assert isinstance(setup, CloudinarySetup), "Invalid setup" + assert isinstance(arguments, CloudinaryUploadArguments), "Invalid arguments" + + try: + # Configure Cloudinary with credentials + cloudinary.config( + cloud_name=setup.cloudinary_cloud_name + if setup.cloudinary_cloud_name != "DEMO_CLOUD_NAME" + else cloudinary_cloud_name, + api_key=setup.cloudinary_api_key + if setup.cloudinary_api_key != "DEMO_API_KEY" + else cloudinary_api_key, + api_secret=setup.cloudinary_api_secret + if setup.cloudinary_api_secret != "DEMO_API_SECRET" + else cloudinary_api_secret, + **(setup.params or {}), + ) + + # Upload the file + upload_params = arguments.upload_params or {} + upload_params["public_id"] = ( + arguments.public_id if arguments.public_id else str(uuid.uuid4()) + ) + + result = cloudinary.uploader.upload(arguments.file, **upload_params) + + meta_data = { + key: value + for key, value in result.items() + if key not in ["secure_url", "public_id"] + } + + if arguments.return_base64: + async with aiohttp.ClientSession() as session: + async with session.get(result["secure_url"]) as response: + if response.status == 200: + content = await response.read() + base64_encoded = base64.b64encode(content).decode("utf-8") + result["base64"] = base64_encoded + else: + raise RuntimeError( + f"Failed to download file from URL: {result['secure_url']}" + ) + return CloudinaryUploadOutput( + url=result["secure_url"], + public_id=result["public_id"], + meta_data=meta_data, + base64=result["base64"] if arguments.return_base64 else None, + ) + + except cloudinary.exceptions.Error as e: + raise RuntimeError(f"Cloudinary error occurred: {e}") + except Exception as e: + raise RuntimeError(f"An unexpected error occurred: {e}") + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def media_edit( + setup: CloudinarySetup, arguments: CloudinaryEditArguments +) -> CloudinaryEditOutput: + """ + Edit media in Cloudinary. + """ + assert isinstance(setup, CloudinarySetup), "Invalid setup" + assert isinstance(arguments, CloudinaryEditArguments), "Invalid arguments" + + try: + # Configure Cloudinary with credentials + cloudinary.config( + cloud_name=setup.cloudinary_cloud_name + if setup.cloudinary_cloud_name != "DEMO_CLOUD_NAME" + else cloudinary_cloud_name, + api_key=setup.cloudinary_api_key + if setup.cloudinary_api_key != "DEMO_API_KEY" + else cloudinary_api_key, + api_secret=setup.cloudinary_api_secret + if setup.cloudinary_api_secret != "DEMO_API_SECRET" + else cloudinary_api_secret, + **(setup.params or {}), + ) + + # Generate transformed URL + transformed_url = cloudinary.utils.cloudinary_url( + arguments.public_id, transformation=arguments.transformation + ) + if not transformed_url or not transformed_url[0]: + return CloudinaryEditOutput( + transformed_url="The transformation failed", + base64=None, + ) + if arguments.return_base64: + async with aiohttp.ClientSession() as session: + async with session.get(transformed_url[0]) as response: + if response.status == 200: + content = await response.read() + base64_encoded = base64.b64encode(content).decode("utf-8") + transformed_url_base64 = base64_encoded + else: + raise RuntimeError( + f"Failed to download file from URL: {transformed_url[0]}" + ) + + return CloudinaryEditOutput( + transformed_url=transformed_url[0], + base64=transformed_url_base64 if arguments.return_base64 else None, + ) + + except cloudinary.exceptions.Error as e: + raise RuntimeError(f"Cloudinary error occurred: {e}") + except Exception as e: + raise RuntimeError(f"An unexpected error occurred: {e}") diff --git a/integrations-service/integrations/utils/integrations/dalle_image_generator.py b/integrations-service/integrations/utils/integrations/dalle_image_generator.py deleted file mode 100644 index e0a6496b8..000000000 --- a/integrations-service/integrations/utils/integrations/dalle_image_generator.py +++ /dev/null @@ -1,22 +0,0 @@ -from langchain_community.utilities.dalle_image_generator import DallEAPIWrapper - -from ...models import DalleImageGeneratorArguments, DalleImageGeneratorSetup - - -async def dalle_image_generator( - setup: DalleImageGeneratorSetup, arguments: DalleImageGeneratorArguments -) -> str: - """ - Generates an image using DALL-E based on a provided prompt. - """ - - assert isinstance(setup, DalleImageGeneratorSetup), "Invalid setup" - assert isinstance(arguments, DalleImageGeneratorArguments), "Invalid arguments" - - # FIXME: Fix OpenAI API Key error - - dalle = DallEAPIWrapper(api_key=setup.api_key) - prompt = arguments.prompt - if not prompt: - raise ValueError("Prompt parameter is required for DALL-E image generation") - return dalle.run(prompt) diff --git a/integrations-service/integrations/utils/integrations/duckduckgo_search.py b/integrations-service/integrations/utils/integrations/duckduckgo_search.py deleted file mode 100644 index a8ea38f56..000000000 --- a/integrations-service/integrations/utils/integrations/duckduckgo_search.py +++ /dev/null @@ -1,15 +0,0 @@ -from langchain_community.tools import DuckDuckGoSearchRun - -from ...models import DuckDuckGoSearchExecutionArguments - - -async def duckduckgo_search(arguments: DuckDuckGoSearchExecutionArguments) -> str: - """ - Performs a web search using DuckDuckGo and returns the results. - """ - - search = DuckDuckGoSearchRun() - query = arguments.query - if not query: - raise ValueError("Query parameter is required for DuckDuckGo search") - return search.run(query) diff --git a/integrations-service/integrations/utils/integrations/email.py b/integrations-service/integrations/utils/integrations/email.py index b0ced9ff5..d7967128f 100644 --- a/integrations-service/integrations/utils/integrations/email.py +++ b/integrations-service/integrations/utils/integrations/email.py @@ -2,14 +2,20 @@ from smtplib import SMTP from beartype import beartype +from tenacity import retry, stop_after_attempt, wait_exponential -from ...models import EmailArguments, EmailOutput, EmailSetup +from ...autogen.Tools import EmailArguments, EmailSetup +from ...env import mailgun_password # Import env to access environment variables +from ...models import EmailOutput -# @beartype -async def send( - setup: EmailSetup, arguments: EmailArguments -) -> EmailOutput: +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def send(setup: EmailSetup, arguments: EmailArguments) -> EmailOutput: """ Sends an email with the provided details. """ @@ -20,6 +26,9 @@ async def send( message["From"] = arguments.from_ message["To"] = arguments.to + if setup.password == "DEMO_PASSWORD": + setup.password = mailgun_password + with SMTP(setup.host, setup.port) as server: server.login(setup.user, setup.password) server.send_message(message) diff --git a/integrations-service/integrations/utils/integrations/ffmpeg.py b/integrations-service/integrations/utils/integrations/ffmpeg.py new file mode 100644 index 000000000..3921e9736 --- /dev/null +++ b/integrations-service/integrations/utils/integrations/ffmpeg.py @@ -0,0 +1,145 @@ +import asyncio +import base64 +import os +import shutil +import tempfile +from functools import lru_cache +from typing import Tuple + +from beartype import beartype +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import FfmpegSearchArguments +from ...models import FfmpegSearchOutput + + +# Cache for format validation +@lru_cache(maxsize=128) +def _sync_validate_format(binary_prefix: bytes) -> Tuple[bool, str]: + """Cached synchronous implementation of format validation""" + signatures = { + # Video formats + b"\x66\x74\x79\x70\x69\x73\x6f\x6d": "video/mp4", # MP4 + b"\x66\x74\x79\x70\x4d\x53\x4e\x56": "video/mp4", # MP4 + b"\x00\x00\x00\x1c\x66\x74\x79\x70": "video/mp4", # MP4 + b"\x1a\x45\xdf\xa3": "video/webm", # WebM + b"\x00\x00\x01\x00": "video/avi", # AVI + b"\x30\x26\xb2\x75": "video/wmv", # WMV + # Audio formats + b"\x49\x44\x33": "audio/mpeg", # MP3 + b"\xff\xfb": "audio/mpeg", # MP3 + b"\x52\x49\x46\x46": "audio/wav", # WAV + b"\x4f\x67\x67\x53": "audio/ogg", # OGG + b"\x66\x4c\x61\x43": "audio/flac", # FLAC + # Image formats + b"\xff\xd8\xff": "image/jpeg", # JPEG + b"\x89\x50\x4e\x47": "image/png", # PNG + b"\x47\x49\x46": "image/gif", # GIF + b"\x49\x49\x2a\x00": "image/tiff", # TIFF + b"\x42\x4d": "image/bmp", # BMP + } + + for signature, mime_type in signatures.items(): + if binary_prefix.startswith(signature): + return True, mime_type + + return False, "application/octet-stream" + + +async def validate_format(binary_data: bytes) -> Tuple[bool, str]: + """Validate file format using file signatures""" + # Only check first 16 bytes for efficiency + binary_prefix = binary_data[:16] + return _sync_validate_format(binary_prefix) + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def bash_cmd(arguments: FfmpegSearchArguments) -> FfmpegSearchOutput: + """Execute a FFmpeg bash command using base64-encoded input data.""" + try: + assert isinstance(arguments, FfmpegSearchArguments), "Invalid arguments" + + # Decode base64 input + try: + input_data = base64.b64decode(arguments.file) + except: + return FfmpegSearchOutput( + fileoutput="Error: Invalid base64 input", result=False, mime_type=None + ) + + # Validate input format + is_valid, input_mime = await validate_format(input_data) + + if not is_valid: + return FfmpegSearchOutput( + fileoutput="Error: Unsupported input file format", + result=False, + mime_type=None, + ) + + # Create temporary directory + temp_dir = tempfile.mkdtemp() + + # Get the output filename from the last argument of the FFmpeg command + cmd_parts = arguments.cmd.split() + output_filename = cmd_parts[-1] # e.g., "output.mp4" + output_path = os.path.join(temp_dir, output_filename) + + # Modify FFmpeg command + for i, part in enumerate(cmd_parts): + if part == "-i" and i + 1 < len(cmd_parts): + cmd_parts[i + 1] = "pipe:0" + cmd_parts[-1] = output_path # Replace the last argument with full output path + cmd = " ".join(cmd_parts) + + # Execute FFmpeg + process = await asyncio.create_subprocess_shell( + cmd, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + # Process FFmpeg output + stdout, stderr = await process.communicate(input=input_data) + success = process.returncode == 0 + + if success and os.path.exists(output_path): + # Read the output file + with open(output_path, "rb") as f: + output_data = f.read() + # Convert to base64 + output_base64 = base64.b64encode(output_data).decode("utf-8") + + _, output_mime = await validate_format(output_data) + + # Clean up + shutil.rmtree(temp_dir) + + return FfmpegSearchOutput( + fileoutput=output_base64, + result=True, + mime_type=output_mime, + ) + + # Clean up in case of failure + shutil.rmtree(temp_dir) + error_msg = stderr.decode() if stderr else "Unknown error occurred" + return FfmpegSearchOutput( + fileoutput=f"Error: FFmpeg processing failed - {error_msg}", + result=False, + mime_type=None, + ) + + except Exception as e: + # Clean up in case of exception + if "temp_dir" in locals(): + shutil.rmtree(temp_dir) + return FfmpegSearchOutput( + fileoutput=f"Error: {str(e)}", result=False, mime_type=None + ) diff --git a/integrations-service/integrations/utils/integrations/gmail/send_mail.py b/integrations-service/integrations/utils/integrations/gmail/send_mail.py deleted file mode 100644 index e1863f280..000000000 --- a/integrations-service/integrations/utils/integrations/gmail/send_mail.py +++ /dev/null @@ -1,6 +0,0 @@ -async def send_mail(arguments: dict) -> str: - """ - Dummy integration for sending an email to a specified recipient with a given subject and message. - """ - - return "Mail sent" diff --git a/integrations-service/integrations/utils/integrations/hacker_news.py b/integrations-service/integrations/utils/integrations/hacker_news.py deleted file mode 100644 index 526024c72..000000000 --- a/integrations-service/integrations/utils/integrations/hacker_news.py +++ /dev/null @@ -1,22 +0,0 @@ -from langchain_community.document_loaders import HNLoader - -from ...models import HackerNewsFetchArguments, HackerNewsFetchOutput - - -async def fetch(arguments: HackerNewsFetchArguments) -> HackerNewsFetchOutput: - """ - Fetches and formats content from a Hacker News thread using the provided URL. - """ - - assert isinstance(arguments, HackerNewsFetchArguments), "Invalid arguments" - - url = arguments.url - if not url: - raise ValueError("URL parameter is required for Hacker News search") - loader = HNLoader(str(url)) - documents = loader.load() - - if not documents: - raise ValueError("No data found for the given URL") - - return HackerNewsFetchOutput(documents=documents) diff --git a/integrations-service/integrations/utils/integrations/llama_parse.py b/integrations-service/integrations/utils/integrations/llama_parse.py new file mode 100644 index 000000000..5d696b38f --- /dev/null +++ b/integrations-service/integrations/utils/integrations/llama_parse.py @@ -0,0 +1,59 @@ +import base64 +import uuid + +from beartype import beartype +from llama_parse import LlamaParse +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import LlamaParseFetchArguments, LlamaParseSetup +from ...env import llama_api_key # Import env to access environment variables +from ...models import LlamaParseFetchOutput + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def parse( + setup: LlamaParseSetup, arguments: LlamaParseFetchArguments +) -> LlamaParseFetchOutput: + """ + Parse and extract content from files using LlamaParse. + """ + + assert isinstance(setup, LlamaParseSetup), "Invalid setup" + assert isinstance(arguments, LlamaParseFetchArguments), "Invalid arguments" + + # Use walrus operator to simplify assignment and condition + if (api_key := setup.llamaparse_api_key) == "DEMO_API_KEY": + api_key = llama_api_key + + # get the additional params + params = ( + {**setup.params, **arguments.params} + if setup.params and arguments.params + else setup.params or arguments.params + ) + + parser = LlamaParse( + api_key=api_key, # Use the local variable instead + **(params if params is not None else None), + ) + + if isinstance(arguments.file, str) and arguments.base64: + extra_info = {"file_name": arguments.filename or str(uuid.uuid4())} + # Parse the document (decode inline) + documents = await parser.aload_data( + base64.b64decode(arguments.file), extra_info=extra_info + ) + else: + if arguments.filename: + extra_info = {"file_name": arguments.filename} + else: + extra_info = None + # Parse the document (decode inline) + documents = await parser.aload_data(arguments.file, extra_info=extra_info) + + return LlamaParseFetchOutput(documents=documents) diff --git a/integrations-service/integrations/utils/integrations/remote_browser.py b/integrations-service/integrations/utils/integrations/remote_browser.py new file mode 100644 index 000000000..2b83c2be6 --- /dev/null +++ b/integrations-service/integrations/utils/integrations/remote_browser.py @@ -0,0 +1,395 @@ +import base64 +import json +from functools import partial +from io import BytesIO +from pathlib import Path +from typing import Any + +from beartype import beartype +from PIL import Image +from playwright.async_api import ( + Browser, + BrowserContext, + Keyboard, + Mouse, + Page, + PlaywrightContextManager, + async_playwright, +) +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import RemoteBrowserArguments, RemoteBrowserSetup +from ...models import RemoteBrowserOutput + +CURSOR_PATH = Path(__file__).parent / "assets" / "cursor-small.png" + + +class PlaywrightActions: + """Class to handle browser automation actions using Playwright.""" + + browser: Browser + page: Page + context: BrowserContext + mouse: Mouse + keyboard: Keyboard + width: int | None + height: int | None + + def __init__( + self, browser: Browser, width: int | None = None, height: int | None = None + ) -> None: + self.browser = browser + self.context = self.browser.contexts[0] + self.page = self.context.pages[0] + self.mouse = self.page.mouse + self.keyboard = self.page.keyboard + self.width, self.height = width, height + + async def _is_initialized(self) -> bool: + """Check if the page is initialized""" + result = bool( + await self._execute_javascript(""" + window.$$julep$$_initialized + """) + ) + + return result + + async def initialize(self, debug: bool = False) -> None: + if debug: + self.page.on("console", lambda msg: print(msg.text)) + + if self.width and self.height: + current_width, current_height = await self._get_screen_size() + if current_width != self.width or current_height != self.height: + await self._set_screen_size(self.width, self.height) + + # Install mouse move event listener + init_script = """ + + // Update mouse coordinates on mouse move + // but only on the top document + if (window === window.parent) + window.addEventListener( + 'DOMContentLoaded', + () => { + const updateMouseCoordinates = (event) => { + window.mouseX = event.clientX; + window.mouseY = event.clientY; + }; + + if (!window.$$julep$$_mouseListenerInitialized) { + window.addEventListener('mousemove', updateMouseCoordinates); + window.addEventListener('mouseup', updateMouseCoordinates); + window.addEventListener('mousedown', updateMouseCoordinates); + window.addEventListener('click', updateMouseCoordinates); + window.addEventListener('dblclick', updateMouseCoordinates); + + window.$$julep$$_mouseListenerInitialized = true; + } + } + ); + """ + await self.page.add_init_script(init_script) + await self.page.evaluate(init_script) + + await self._reset_mouse() + + async def _reset_mouse(self) -> None: + # Wait for the page to be fully loaded + await self._wait_for_load() + if not await self._is_initialized(): + await self.page.mouse.move(self.width // 2, self.height // 2) + await self.page.evaluate(""" + window.$$julep$$_initialized = true; + """) + + # @staticmethod + # def _with_error_and_screenshot(f): + # @wraps(f) + # async def wrapper(self: "PlaywrightActions", *args, **kwargs): + # try: + # result: RemoteBrowserOutput = await f(self, *args, **kwargs) + # await self._wait_for_load() + + # screenshot: RemoteBrowserOutput = await self.take_screenshot() + + # return RemoteBrowserOutput( + # output=result.output, + # base64_image=screenshot.base64_image, + # system=result.system or f.__name__, + # ) + + # except Exception as e: + # return RemoteBrowserOutput(error=str(e)) + + # return wrapper + + async def _get_screen_size(self) -> tuple[int, int]: + """Get the current browser viewport size""" + + viewport: dict[str, int] | None = self.page.viewport_size + if viewport is None: + return (0, 0) + + return (viewport["width"], viewport["height"]) + + async def _set_screen_size(self, width: int, height: int) -> None: + """Set the current browser viewport size""" + + await self.page.set_viewport_size(dict(width=width, height=height)) + + async def _wait_for_load( + self, event: str = "domcontentloaded", timeout: int = 0 + ) -> None: + """Wait for document to be fully loaded""" + await self.page.wait_for_load_state(event, timeout=timeout) + + async def _execute_javascript(self, script: str, *args) -> Any: + """Execute JavaScript code and return the result""" + return await self.page.evaluate(script, *args) + + async def _set_window_vars(self, variables: dict[str, Any]) -> None: + """Set variables in the window scope""" + json_str = json.dumps(variables) + script = """ + const vars = JSON.parse(arguments[0]); + for (const [key, value] of Object.entries(vars)) { + window[key] = value; + } + """ + await self._execute_javascript(script, json_str) + + async def _get_mouse_coordinates(self) -> tuple[int, int]: + """Get current mouse coordinates""" + result = await self._execute_javascript(""" + [window.mouseX, window.mouseY] + """) + + return result[0], result[1] + + async def _get_element_coordinates(self, selector: str) -> tuple[int, int]: + """Get the coordinates of an element""" + element = await self.page.query_selector(selector) + if element: + box = await element.bounding_box() + return (box["x"], box["y"]) + raise Exception(f"Element not found: {selector}") + + def _overlay_cursor(self, screenshot_bytes: bytes, x: int, y: int) -> bytes: + """Overlay the cursor image on the screenshot at the specified coordinates.""" + # Load the screenshot from bytes + screenshot = Image.open(BytesIO(screenshot_bytes)).convert("RGBA") + + # Load the cursor image + cursor = Image.open( + "./integrations/utils/integrations/assets/cursor-small.png" + ).convert("RGBA") + + # Create a copy of the screenshot to overlay the cursor + combined = screenshot.copy() + combined.paste(cursor, (x, y), cursor) + + # Save the combined image to bytes + output = BytesIO() + combined.save(output, format="PNG") + return output.getvalue() + + # --- + # Actions + + async def navigate(self, url: str) -> RemoteBrowserOutput: + """Navigate to a specific URL""" + await self.page.goto(url) + await self._reset_mouse() + + return RemoteBrowserOutput( + output=url, + ) + + async def refresh(self) -> RemoteBrowserOutput: + """Refresh the current page""" + await self.page.reload() + await self._reset_mouse() + + return RemoteBrowserOutput( + output="Refreshed page", + ) + + async def cursor_position(self) -> RemoteBrowserOutput: + """Get current mouse coordinates""" + x, y = await self._get_mouse_coordinates() + return RemoteBrowserOutput( + output=f"X={x}, Y={y}", + ) + + async def press_key(self, key_combination: str) -> RemoteBrowserOutput: + """Press a key or key combination""" + # Split combination into individual keys + keys = key_combination.split("+") + keys = [ + "Enter" + if k == "Return" + else "Control" + if k == "ctrl" + else "PageDown" + if k == "Page_Down" + else k + for k in keys + ] + + # Press modifier keys first + for key in keys[:-1]: + await self.page.keyboard.down(key) + + # Press and release the last key + await self.page.keyboard.press(keys[-1]) + + # Release modifier keys in reverse order + for key in reversed(keys[:-1]): + await self.page.keyboard.up(key) + + return RemoteBrowserOutput( + output=f"Pressed {key_combination}", + ) + + async def type_text(self, text: str) -> RemoteBrowserOutput: + """Type a string of text""" + await self.page.keyboard.type(text) + + return RemoteBrowserOutput( + output=f"Typed {text}", + ) + + async def mouse_move(self, coordinate: tuple[int, int]) -> RemoteBrowserOutput: + """Move mouse to specified coordinates""" + await self.mouse.move(*coordinate) + + return RemoteBrowserOutput( + output=f"Moved mouse to {coordinate}", + ) + + async def left_click(self) -> RemoteBrowserOutput: + """Perform left mouse click""" + x, y = await self._get_mouse_coordinates() + await self.mouse.click(x, y) + + return RemoteBrowserOutput( + output="Left clicked", + ) + + async def left_click_drag(self, coordinate: tuple[int, int]) -> RemoteBrowserOutput: + """Click and drag to specified coordinates""" + await self.mouse.down() + await self.mouse.move(*coordinate) + await self.mouse.up() + + return RemoteBrowserOutput( + output=f"Left clicked and dragged to {coordinate}", + ) + + async def right_click(self) -> RemoteBrowserOutput: + """Perform right mouse click""" + x, y = await self._get_mouse_coordinates() + await self.mouse.click(x, y, button="right") + + return RemoteBrowserOutput( + output="Right clicked", + ) + + async def middle_click(self) -> RemoteBrowserOutput: + """Perform middle mouse click""" + x, y = await self._get_mouse_coordinates() + await self.mouse.click(x, y, button="middle") + + return RemoteBrowserOutput( + output="Middle clicked", + ) + + async def double_click(self) -> RemoteBrowserOutput: + """Perform double click""" + x, y = await self._get_mouse_coordinates() + await self.mouse.dblclick(x, y) + + return RemoteBrowserOutput( + output="Double clicked", + ) + + async def take_screenshot(self, filename: str | None = None) -> RemoteBrowserOutput: + """Take a screenshot of the current browser window""" + try: + screenshot = await self.page.screenshot() + except Exception as e: + return RemoteBrowserOutput(error=str(e), system="take_screenshot") + + x, y = await self._get_mouse_coordinates() + screenshot = self._overlay_cursor(screenshot, x, y) + + if filename: + with open(filename, "wb") as f: + f.write(screenshot) + + encoded = base64.b64encode(screenshot).decode("utf-8") + + return RemoteBrowserOutput( + base64_image=f"data:image/png;base64,{encoded}", + system="take_screenshot", + ) + + async def perform_action( + self, + action: str, + coordinate: tuple[int, int] | None = None, + text: str | None = None, + ) -> RemoteBrowserOutput: + """Perform a specified automation action""" + try: + actions = { + # Anthropic + "key": partial(self.press_key, text), + "type": partial(self.type_text, text), + "mouse_move": partial(self.mouse_move, coordinate), + "left_click": self.left_click, + "left_click_drag": partial(self.left_click_drag, coordinate), + "right_click": self.right_click, + "middle_click": self.middle_click, + "double_click": self.double_click, + "screenshot": self.take_screenshot, + "cursor_position": self.cursor_position, + # + # Additional + "navigate": partial(self.navigate, text), + "refresh": self.refresh, + } + + if action not in actions: + raise ValueError(f"Invalid action: {action}") + + return await actions[action]() + + except Exception as e: + raise Exception(f"Error performing action {action}: {str(e)}") + + +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def perform_action( + setup: RemoteBrowserSetup, arguments: RemoteBrowserArguments +) -> RemoteBrowserOutput: + p: PlaywrightContextManager = await async_playwright().start() + connect_url = setup.connect_url if setup.connect_url else arguments.connect_url + browser = await p.chromium.connect_over_cdp(connect_url) + + automation = PlaywrightActions(browser, width=setup.width, height=setup.height) + + await automation.initialize() + + return await automation.perform_action( + action=arguments.action, + coordinate=arguments.coordinate, + text=arguments.text, + ) diff --git a/integrations-service/integrations/utils/integrations/request.py b/integrations-service/integrations/utils/integrations/request.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/integrations-service/integrations/utils/integrations/spider.py b/integrations-service/integrations/utils/integrations/spider.py index a355e2347..8cf5e30cb 100644 --- a/integrations-service/integrations/utils/integrations/spider.py +++ b/integrations-service/integrations/utils/integrations/spider.py @@ -1,9 +1,28 @@ +import asyncio + +from beartype import beartype from langchain_community.document_loaders import SpiderLoader +from tenacity import retry, stop_after_attempt, wait_exponential + +from ...autogen.Tools import SpiderFetchArguments, SpiderSetup +from ...env import spider_api_key # Import env to access environment variables +from ...models import SpiderFetchOutput + -from ...models import SpiderFetchArguments, SpiderFetchOutput, SpiderSetup +# Spider client instances +def get_spider_client(api_key: str, **kwargs) -> SpiderLoader: + return SpiderLoader(api_key=api_key, **kwargs) -async def crawl(setup: SpiderSetup, arguments: SpiderFetchArguments) -> SpiderFetchOutput: +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def crawl( + setup: SpiderSetup, arguments: SpiderFetchArguments +) -> SpiderFetchOutput: """ Fetches data from a specified URL. """ @@ -11,18 +30,18 @@ async def crawl(setup: SpiderSetup, arguments: SpiderFetchArguments) -> SpiderFe assert isinstance(setup, SpiderSetup), "Invalid setup" assert isinstance(arguments, SpiderFetchArguments), "Invalid arguments" - url = arguments.url - - if not url: - raise ValueError("URL parameter is required for spider") + api_key = ( + setup.spider_api_key + if setup.spider_api_key != "DEMO_API_KEY" + else spider_api_key + ) - spider_loader = SpiderLoader( - api_key=setup.spider_api_key, - url=str(url), + spider_loader = get_spider_client( + api_key=api_key, + url=str(arguments.url), mode=arguments.mode, params=arguments.params, ) - documents = spider_loader.load() - + documents = await asyncio.to_thread(spider_loader.load) return SpiderFetchOutput(documents=documents) diff --git a/integrations-service/integrations/utils/integrations/weather.py b/integrations-service/integrations/utils/integrations/weather.py index e9393bc09..19e6c659e 100644 --- a/integrations-service/integrations/utils/integrations/weather.py +++ b/integrations-service/integrations/utils/integrations/weather.py @@ -1,8 +1,18 @@ +from beartype import beartype from langchain_community.utilities import OpenWeatherMapAPIWrapper +from tenacity import retry, stop_after_attempt, wait_exponential -from ...models import WeatherGetArguments, WeatherGetOutput, WeatherSetup +from ...autogen.Tools import WeatherGetArguments, WeatherSetup +from ...env import openweather_api_key # Import env to access environment variables +from ...models import WeatherGetOutput +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) async def get(setup: WeatherSetup, arguments: WeatherGetArguments) -> WeatherGetOutput: """ Fetches weather data for a specified location using OpenWeatherMap API. @@ -14,10 +24,12 @@ async def get(setup: WeatherSetup, arguments: WeatherGetArguments) -> WeatherGet location = arguments.location openweathermap_api_key = setup.openweathermap_api_key + if openweathermap_api_key == "DEMO_API_KEY": + openweathermap_api_key = openweather_api_key + if not location: raise ValueError("Location parameter is required for weather data") weather = OpenWeatherMapAPIWrapper(openweathermap_api_key=openweathermap_api_key) result = weather.run(location) return WeatherGetOutput(result=result) - diff --git a/integrations-service/integrations/utils/integrations/wikipedia.py b/integrations-service/integrations/utils/integrations/wikipedia.py index aa53b5515..235d9512a 100644 --- a/integrations-service/integrations/utils/integrations/wikipedia.py +++ b/integrations-service/integrations/utils/integrations/wikipedia.py @@ -1,9 +1,20 @@ +from beartype import beartype from langchain_community.document_loaders import WikipediaLoader +from tenacity import retry, stop_after_attempt, wait_exponential -from ...models import WikipediaSearchArguments, WikipediaSearchOutput +from ...autogen.Tools import WikipediaSearchArguments +from ...models import WikipediaSearchOutput -def search(arguments: WikipediaSearchArguments) -> WikipediaSearchOutput: +@beartype +@retry( + wait=wait_exponential(multiplier=1, min=4, max=10), + reraise=True, + stop=stop_after_attempt(4), +) +async def search( + arguments: WikipediaSearchArguments, +) -> WikipediaSearchOutput: """ Searches Wikipedia for a given query and returns formatted results. """ diff --git a/integrations-service/integrations/web.py b/integrations-service/integrations/web.py index ae91a53ca..905d9abc1 100644 --- a/integrations-service/integrations/web.py +++ b/integrations-service/integrations/web.py @@ -1,20 +1,35 @@ +import asyncio import logging +import os from typing import Any, Callable -import fire import uvicorn +import uvloop from fastapi import FastAPI, Request, status from fastapi.exceptions import HTTPException, RequestValidationError +from fastapi.middleware.gzip import GZipMiddleware from fastapi.responses import JSONResponse -from .routers import execution_router, integrations_router +from .routers.execution.router import router as execution_router +from .routers.integrations.router import router as integrations_router -app: FastAPI = FastAPI() +app: FastAPI = FastAPI( + title="Integrations Service", + docs_url="/docs", + redoc_url="/redoc", + openapi_url="/openapi.json", + default_response_class=JSONResponse, +) + +# Add GZIP compression +app.add_middleware(GZipMiddleware, minimum_size=1000) # Add routers app.include_router(integrations_router) app.include_router(execution_router) +# Optimize event loop policy +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) logger: logging.Logger = logging.getLogger(__name__) @@ -67,17 +82,17 @@ def main( timeout_keep_alive=30, workers=None, log_level="info", + reload=bool(os.environ.get("RELOAD")), ) -> None: + print(f"Reload: {reload}") + uvicorn.run( - app, + "integrations.web:app", host=host, port=port, log_level=log_level, timeout_keep_alive=timeout_keep_alive, backlog=backlog, workers=workers, + reload=reload, ) - - -if __name__ == "__main__": - fire.Fire(main) diff --git a/integrations-service/poetry.lock b/integrations-service/poetry.lock index bc294a56f..2fae96c35 100644 --- a/integrations-service/poetry.lock +++ b/integrations-service/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -13,102 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.9" +version = "3.11.7" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8b3fb28a9ac8f2558760d8e637dbf27aef1e8b7f1d221e8669a1074d1a266bb2"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91aa966858593f64c8a65cdefa3d6dc8fe3c2768b159da84c1ddbbb2c01ab4ef"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63649309da83277f06a15bbdc2a54fbe75efb92caa2c25bb57ca37762789c746"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e7fabedb3fe06933f47f1538df7b3a8d78e13d7167195f51ca47ee12690373"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c070430fda1a550a1c3a4c2d7281d3b8cfc0c6715f616e40e3332201a253067"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d0a4901b27272ae54e42067bc4b9a90e619a690b4dc43ea5950eb3070afc32"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fec5fac7aea6c060f317f07494961236434928e6f4374e170ef50b3001e14581"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:172ad884bb61ad31ed7beed8be776eb17e7fb423f1c1be836d5cb357a096bf12"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d646fdd74c25bbdd4a055414f0fe32896c400f38ffbdfc78c68e62812a9e0257"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e86260b76786c28acf0b5fe31c8dca4c2add95098c709b11e8c35b424ebd4f5b"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d7cafc11d70fdd8801abfc2ff276744ae4cb39d8060b6b542c7e44e5f2cfc2"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc262c3df78c8ff6020c782d9ce02e4bcffe4900ad71c0ecdad59943cba54442"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:482c85cf3d429844396d939b22bc2a03849cb9ad33344689ad1c85697bcba33a"}, - {file = "aiohttp-3.10.9-cp310-cp310-win32.whl", hash = "sha256:aeebd3061f6f1747c011e1d0b0b5f04f9f54ad1a2ca183e687e7277bef2e0da2"}, - {file = "aiohttp-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:fa430b871220dc62572cef9c69b41e0d70fcb9d486a4a207a5de4c1f25d82593"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16e6a51d8bc96b77f04a6764b4ad03eeef43baa32014fce71e882bd71302c7e4"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8bd9125dd0cc8ebd84bff2be64b10fdba7dc6fd7be431b5eaf67723557de3a31"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dcf354661f54e6a49193d0b5653a1b011ba856e0b7a76bda2c33e4c6892f34ea"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42775de0ca04f90c10c5c46291535ec08e9bcc4756f1b48f02a0657febe89b10"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d1e4185c5d7187684d41ebb50c9aeaaaa06ca1875f4c57593071b0409d2444"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2695c61cf53a5d4345a43d689f37fc0f6d3a2dc520660aec27ec0f06288d1f9"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a3f063b41cc06e8d0b3fcbbfc9c05b7420f41287e0cd4f75ce0a1f3d80729e6"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d37f4718002863b82c6f391c8efd4d3a817da37030a29e2682a94d2716209de"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2746d8994ebca1bdc55a1e998feff4e94222da709623bb18f6e5cfec8ec01baf"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6f3c6648aa123bcd73d6f26607d59967b607b0da8ffcc27d418a4b59f4c98c7c"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:558b3d223fd631ad134d89adea876e7fdb4c93c849ef195049c063ada82b7d08"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4e6cb75f8ddd9c2132d00bc03c9716add57f4beff1263463724f6398b813e7eb"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:608cecd8d58d285bfd52dbca5b6251ca8d6ea567022c8a0eaae03c2589cd9af9"}, - {file = "aiohttp-3.10.9-cp311-cp311-win32.whl", hash = "sha256:36d4fba838be5f083f5490ddd281813b44d69685db910907636bc5dca6322316"}, - {file = "aiohttp-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:8be1a65487bdfc285bd5e9baf3208c2132ca92a9b4020e9f27df1b16fab998a9"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4fd16b30567c5b8e167923be6e027eeae0f20cf2b8a26b98a25115f28ad48ee0"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40ff5b7660f903dc587ed36ef08a88d46840182d9d4b5694e7607877ced698a1"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4edc3fd701e2b9a0d605a7b23d3de4ad23137d23fc0dbab726aa71d92f11aaaf"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e525b69ee8a92c146ae5b4da9ecd15e518df4d40003b01b454ad694a27f498b5"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5002a02c17fcfd796d20bac719981d2fca9c006aac0797eb8f430a58e9d12431"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4ceeae2fb8cabdd1b71c82bfdd39662473d3433ec95b962200e9e752fb70d0"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6e395c3d1f773cf0651cd3559e25182eb0c03a2777b53b4575d8adc1149c6e9"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbdb8def5268f3f9cd753a265756f49228a20ed14a480d151df727808b4531dd"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f82ace0ec57c94aaf5b0e118d4366cff5889097412c75aa14b4fd5fc0c44ee3e"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ebdc3b3714afe1b134b3bbeb5f745eed3ecbcff92ab25d80e4ef299e83a5465"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f9ca09414003c0e96a735daa1f071f7d7ed06962ef4fa29ceb6c80d06696d900"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1298b854fd31d0567cbb916091be9d3278168064fca88e70b8468875ef9ff7e7"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60ad5b8a7452c0f5645c73d4dad7490afd6119d453d302cd5b72b678a85d6044"}, - {file = "aiohttp-3.10.9-cp312-cp312-win32.whl", hash = "sha256:1a0ee6c0d590c917f1b9629371fce5f3d3f22c317aa96fbdcce3260754d7ea21"}, - {file = "aiohttp-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:c46131c6112b534b178d4e002abe450a0a29840b61413ac25243f1291613806a"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bd9f3eac515c16c4360a6a00c38119333901b8590fe93c3257a9b536026594d"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8cc0d13b4e3b1362d424ce3f4e8c79e1f7247a00d792823ffd640878abf28e56"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba1a599255ad6a41022e261e31bc2f6f9355a419575b391f9655c4d9e5df5ff5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:776e9f3c9b377fcf097c4a04b241b15691e6662d850168642ff976780609303c"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8debb45545ad95b58cc16c3c1cc19ad82cffcb106db12b437885dbee265f0ab5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2555e4949c8d8782f18ef20e9d39730d2656e218a6f1a21a4c4c0b56546a02e"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c54dc329cd44f7f7883a9f4baaefe686e8b9662e2c6c184ea15cceee587d8d69"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e709d6ac598c5416f879bb1bae3fd751366120ac3fa235a01de763537385d036"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:17c272cfe7b07a5bb0c6ad3f234e0c336fb53f3bf17840f66bd77b5815ab3d16"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c21c82df33b264216abffff9f8370f303dab65d8eee3767efbbd2734363f677"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9331dd34145ff105177855017920dde140b447049cd62bb589de320fd6ddd582"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ac3196952c673822ebed8871cf8802e17254fff2a2ed4835d9c045d9b88c5ec7"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2c33fa6e10bb7ed262e3ff03cc69d52869514f16558db0626a7c5c61dde3c29f"}, - {file = "aiohttp-3.10.9-cp313-cp313-win32.whl", hash = "sha256:a14e4b672c257a6b94fe934ee62666bacbc8e45b7876f9dd9502d0f0fe69db16"}, - {file = "aiohttp-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:a35ed3d03910785f7d9d6f5381f0c24002b2b888b298e6f941b2fc94c5055fcd"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f392ef50e22c31fa49b5a46af7f983fa3f118f3eccb8522063bee8bfa6755f8"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d1f5c9169e26db6a61276008582d945405b8316aae2bb198220466e68114a0f5"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8d9d10d10ec27c0d46ddaecc3c5598c4db9ce4e6398ca872cdde0525765caa2f"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97273a52d7f89a75b11ec386f786d3da7723d7efae3034b4dda79f6f093edc1"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d271f770b52e32236d945911b2082f9318e90ff835d45224fa9e28374303f729"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7003f33f5f7da1eb02f0446b0f8d2ccf57d253ca6c2e7a5732d25889da82b517"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6e00c8a92e7663ed2be6fcc08a2997ff06ce73c8080cd0df10cc0321a3168d7"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a61df62966ce6507aafab24e124e0c3a1cfbe23c59732987fc0fd0d71daa0b88"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:60555211a006d26e1a389222e3fab8cd379f28e0fbf7472ee55b16c6c529e3a6"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d15a29424e96fad56dc2f3abed10a89c50c099f97d2416520c7a543e8fddf066"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a19caae0d670771ea7854ca30df76f676eb47e0fd9b2ee4392d44708f272122d"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:99f9678bf0e2b1b695e8028fedac24ab6770937932eda695815d5a6618c37e04"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2914caa46054f3b5ff910468d686742ff8cff54b8a67319d75f5d5945fd0a13d"}, - {file = "aiohttp-3.10.9-cp38-cp38-win32.whl", hash = "sha256:0bc059ecbce835630e635879f5f480a742e130d9821fbe3d2f76610a6698ee25"}, - {file = "aiohttp-3.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:e883b61b75ca6efc2541fcd52a5c8ccfe288b24d97e20ac08fdf343b8ac672ea"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcd546782d03181b0b1d20b43d612429a90a68779659ba8045114b867971ab71"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:85711eec2d875cd88c7eb40e734c4ca6d9ae477d6f26bd2b5bb4f7f60e41b156"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02d1d6610588bcd743fae827bd6f2e47e0d09b346f230824b4c6fb85c6065f9c"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3668d0c2a4d23fb136a753eba42caa2c0abbd3d9c5c87ee150a716a16c6deec1"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7c071235a47d407b0e93aa6262b49422dbe48d7d8566e1158fecc91043dd948"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac74e794e3aee92ae8f571bfeaa103a141e409863a100ab63a253b1c53b707eb"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bbf94d4a0447705b7775417ca8bb8086cc5482023a6e17cdc8f96d0b1b5aba6"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb0b2d5d51f96b6cc19e6ab46a7b684be23240426ae951dcdac9639ab111b45e"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e83dfefb4f7d285c2d6a07a22268344a97d61579b3e0dce482a5be0251d672ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f0a44bb40b6aaa4fb9a5c1ee07880570ecda2065433a96ccff409c9c20c1624a"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c2b627d3c8982691b06d89d31093cee158c30629fdfebe705a91814d49b554f8"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:03690541e4cc866eef79626cfa1ef4dd729c5c1408600c8cb9e12e1137eed6ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad3675c126f2a95bde637d162f8231cff6bc0bc9fbe31bd78075f9ff7921e322"}, - {file = "aiohttp-3.10.9-cp39-cp39-win32.whl", hash = "sha256:1321658f12b6caffafdc35cfba6c882cb014af86bef4e78c125e7e794dfb927b"}, - {file = "aiohttp-3.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:9fdf5c839bf95fc67be5794c780419edb0dbef776edcfc6c2e5e2ffd5ee755fa"}, - {file = "aiohttp-3.10.9.tar.gz", hash = "sha256:143b0026a9dab07a05ad2dd9e46aa859bffdd6348ddc5967b42161168c24f857"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2"}, + {file = "aiohttp-3.11.7-cp310-cp310-win32.whl", hash = "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341"}, + {file = "aiohttp-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720"}, + {file = "aiohttp-3.11.7-cp311-cp311-win32.whl", hash = "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847"}, + {file = "aiohttp-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd"}, + {file = "aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8"}, + {file = "aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:241a6ca732d2766836d62c58c49ca7a93d08251daef0c1e3c850df1d1ca0cbc4"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa3705a8d14de39898da0fbad920b2a37b7547c3afd2a18b9b81f0223b7d0f68"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9acfc7f652b31853eed3b92095b0acf06fd5597eeea42e939bd23a17137679d5"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcefcf2915a2dbdbce37e2fc1622129a1918abfe3d06721ce9f6cdac9b6d2eaa"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1f6490dd1862af5aae6cfcf2a274bffa9a5b32a8f5acb519a7ecf5a99a88866"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac5462582d6561c1c1708853a9faf612ff4e5ea5e679e99be36143d6eabd8e"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1a6309005acc4b2bcc577ba3b9169fea52638709ffacbd071f3503264620da"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b973cce96793725ef63eb449adfb74f99c043c718acb76e0d2a447ae369962"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ce91a24aac80de6be8512fb1c4838a9881aa713f44f4e91dd7bb3b34061b497d"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:875f7100ce0e74af51d4139495eec4025affa1a605280f23990b6434b81df1bd"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c171fc35d3174bbf4787381716564042a4cbc008824d8195eede3d9b938e29a8"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ee9afa1b0d2293c46954f47f33e150798ad68b78925e3710044e0d67a9487791"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8360c7cc620abb320e1b8d603c39095101391a82b1d0be05fb2225471c9c5c52"}, + {file = "aiohttp-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7a9318da4b4ada9a67c1dd84d1c0834123081e746bee311a16bb449f363d965e"}, + {file = "aiohttp-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:fc6da202068e0a268e298d7cd09b6e9f3997736cd9b060e2750963754552a0a9"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:17829f37c0d31d89aa6b8b010475a10233774771f9b6dc2cc352ea4f8ce95d9a"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6177077a31b1aecfc3c9070bd2f11419dbb4a70f30f4c65b124714f525c2e48"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:badda65ac99555791eed75e234afb94686ed2317670c68bff8a4498acdaee935"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de6466b9d742b4ee56fe1b2440706e225eb48c77c63152b1584864a236e7a50"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b0cc74d5a882c9dacaeeccc1444f0233212b6f5be8bc90833feef1e1ce14b9"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c7af3e50e5903d21d7b935aceed901cc2475463bc16ddd5587653548661fdb"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c63f898f683d1379b9be5afc3dd139e20b30b0b1e0bf69a3fc3681f364cf1629"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdadc3f6a32d6eca45f9a900a254757fd7855dfb2d8f8dcf0e88f0fae3ff8eb1"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d329300fb23e14ed1f8c6d688dfd867d1dcc3b1d7cd49b7f8c5b44e797ce0932"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5578cf40440eafcb054cf859964bc120ab52ebe0e0562d2b898126d868749629"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7b2f8107a3c329789f3c00b2daad0e35f548d0a55cda6291579136622099a46e"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:43dd89a6194f6ab02a3fe36b09e42e2df19c211fc2050ce37374d96f39604997"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2fa6fc7cc865d26ff42480ac9b52b8c9b7da30a10a6442a9cdf429de840e949"}, + {file = "aiohttp-3.11.7-cp39-cp39-win32.whl", hash = "sha256:a7d9a606355655617fee25dd7e54d3af50804d002f1fd3118dd6312d26692d70"}, + {file = "aiohttp-3.11.7-cp39-cp39-win_amd64.whl", hash = "sha256:53c921b58fdc6485d6b2603e0132bb01cd59b8f0620ffc0907f525e0ba071687"}, + {file = "aiohttp-3.11.7.tar.gz", hash = "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668"}, ] [package.dependencies] @@ -117,7 +102,8 @@ aiosignal = ">=1.1.2" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.12.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -149,13 +135,13 @@ files = [ [[package]] name = "anyio" -version = "4.6.0" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, - {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -164,19 +150,38 @@ sniffio = ">=1.1" [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] [[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." +name = "argcomplete" +version = "3.5.1" +description = "Bash tab completion for argparse" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" +files = [ + {file = "argcomplete-3.5.1-py3-none-any.whl", hash = "sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363"}, + {file = "argcomplete-3.5.1.tar.gz", hash = "sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "arxiv" +version = "2.1.3" +description = "Python wrapper for the arXiv API: https://arxiv.org/help/api/" +optional = false +python-versions = ">=3.7" files = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, + {file = "arxiv-2.1.3-py3-none-any.whl", hash = "sha256:6f43673ab770a9e848d7d4fc1894824df55edeac3c3572ea280c9ba2e3c0f39f"}, + {file = "arxiv-2.1.3.tar.gz", hash = "sha256:32365221994d2cf05657c1fadf63a26efc8ccdec18590281ee03515bfef8bc4e"}, ] +[package.dependencies] +feedparser = ">=6.0.10,<6.1.0" +requests = ">=2.32.0,<2.33.0" + [[package]] name = "attrs" version = "24.2.0" @@ -235,15 +240,59 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "browserbase" -version = "0.3.0" +version = "0.3.6" description = "Browserbase Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "browserbase-0.3.0-py3-none-any.whl", hash = "sha256:16fe6f0b1fc55aca050bbabf76cd17d83ee1a798d9bba274b09421ca120b5ec3"}, - {file = "browserbase-0.3.0.tar.gz", hash = "sha256:2ec31641fab0a9ec3b2f23ed0244f01e3b35423806c1c6665d68706133958b07"}, + {file = "browserbase-0.3.6-py3-none-any.whl", hash = "sha256:da3338302c34ecb6abda0534bc16663c7049a8bef2694db5a93ef05d2ba1d3c3"}, + {file = "browserbase-0.3.6.tar.gz", hash = "sha256:1d20d9490304e27c4cf84a9c119b0bce98d87ceaec298acac4a29c47058d367e"}, ] [package.dependencies] @@ -262,103 +311,197 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -375,6 +518,42 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "cloudinary" +version = "1.41.0" +description = "Python and Django SDK for Cloudinary" +optional = false +python-versions = "*" +files = [ + {file = "cloudinary-1.41.0.tar.gz", hash = "sha256:e189739a796a7d2ad15c19971741d33a9300816b16c0282b4b14ccf1dd2948c0"}, +] + +[package.dependencies] +certifi = "*" +six = "*" +urllib3 = ">=1.26.5" + +[package.extras] +dev = ["tox"] + +[[package]] +name = "cloudinary" +version = "1.41.0" +description = "Python and Django SDK for Cloudinary" +optional = false +python-versions = "*" +files = [ + {file = "cloudinary-1.41.0.tar.gz", hash = "sha256:e189739a796a7d2ad15c19971741d33a9300816b16c0282b4b14ccf1dd2948c0"}, +] + +[package.dependencies] +certifi = "*" +six = "*" +urllib3 = ">=1.26.5" + +[package.extras] +dev = ["tox"] + [[package]] name = "colorama" version = "0.4.6" @@ -386,6 +565,80 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.6.8" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, + {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, + {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, + {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, + {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, + {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, + {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, + {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, + {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, + {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, + {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, + {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, + {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, + {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, + {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, + {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, + {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, + {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, + {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, + {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, +] + +[package.extras] +toml = ["tomli"] + [[package]] name = "dataclasses-json" version = "0.6.7" @@ -401,6 +654,62 @@ files = [ marshmallow = ">=3.18.0,<4.0.0" typing-inspect = ">=0.4.0,<1" +[[package]] +name = "datamodel-code-generator" +version = "0.26.3" +description = "Datamodel Code Generator" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "datamodel_code_generator-0.26.3-py3-none-any.whl", hash = "sha256:f1f8f1cef14f138fa239f987d4640837bb68d53c5f08d8673a7bde275b929fd8"}, + {file = "datamodel_code_generator-0.26.3.tar.gz", hash = "sha256:b58e0800eb6448e1d1df02f4586207c1e3631c4a188531d154b00b3cf2f95fd8"}, +] + +[package.dependencies] +argcomplete = ">=1.10,<4.0" +black = ">=19.10b0" +genson = ">=1.2.1,<2.0" +inflect = ">=4.1.0,<6.0" +isort = ">=4.3.21,<6.0" +jinja2 = ">=2.10.1,<4.0" +packaging = "*" +pydantic = {version = ">=1.10.0,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.12\" and python_version < \"4.0\""} +pyyaml = ">=6.0.1" + +[package.extras] +debug = ["PySnooper (>=0.4.1,<2.0.0)"] +graphql = ["graphql-core (>=3.2.3,<4.0.0)"] +http = ["httpx"] +validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] + +[[package]] +name = "deprecated" +version = "1.2.15" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, + {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] + +[[package]] +name = "dirtyjson" +version = "1.0.8" +description = "JSON decoder for Python that can extract data from the muck" +optional = false +python-versions = "*" +files = [ + {file = "dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53"}, + {file = "dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd"}, +] + [[package]] name = "distro" version = "1.9.0" @@ -434,18 +743,18 @@ wmi = ["wmi (>=1.5.1)"] [[package]] name = "duckduckgo-search" -version = "6.2.13" +version = "6.3.7" description = "Search for words, documents, images, news, maps and text translation using the DuckDuckGo.com search engine." optional = false python-versions = ">=3.8" files = [ - {file = "duckduckgo_search-6.2.13-py3-none-any.whl", hash = "sha256:a6618fb2744fa1d081b1bf2e47ef8051de993276a15c20f4e879a150726472de"}, - {file = "duckduckgo_search-6.2.13.tar.gz", hash = "sha256:f89a9782f0f47d18c01a761c83453d0aef7a4335d1b6466fc47709652d5ca791"}, + {file = "duckduckgo_search-6.3.7-py3-none-any.whl", hash = "sha256:6a831a27977751e8928222f04c99a5d069ff80e2a7c78b699c9b9ac6cb48c41b"}, + {file = "duckduckgo_search-6.3.7.tar.gz", hash = "sha256:53d84966429a6377647e2a1ea7224b657575c7a4d506729bdb837e4ee12915ed"}, ] [package.dependencies] click = ">=8.1.7" -primp = ">=0.6.3" +primp = ">=0.8.1" [package.extras] dev = ["mypy (>=1.11.1)", "pytest (>=8.3.1)", "pytest-asyncio (>=0.23.8)", "ruff (>=0.6.1)"] @@ -466,26 +775,91 @@ files = [ dnspython = ">=2.0.0" idna = ">=2.0.0" +[[package]] +name = "environs" +version = "11.2.1" +description = "simplified environment variable parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "environs-11.2.1-py3-none-any.whl", hash = "sha256:9d2080cf25807a26fc0d4301e2d7b62c64fbf547540f21e3a30cc02bc5fbe948"}, + {file = "environs-11.2.1.tar.gz", hash = "sha256:e068ae3174cef52ba4b95ead22e639056a02465f616e62323e04ae08e86a75a4"}, +] + +[package.dependencies] +marshmallow = ">=3.13.0" +python-dotenv = "*" + +[package.extras] +dev = ["environs[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +tests = ["environs[django]", "pytest"] + +[[package]] +name = "environs" +version = "11.2.1" +description = "simplified environment variable parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "environs-11.2.1-py3-none-any.whl", hash = "sha256:9d2080cf25807a26fc0d4301e2d7b62c64fbf547540f21e3a30cc02bc5fbe948"}, + {file = "environs-11.2.1.tar.gz", hash = "sha256:e068ae3174cef52ba4b95ead22e639056a02465f616e62323e04ae08e86a75a4"}, +] + +[package.dependencies] +marshmallow = ">=3.13.0" +python-dotenv = "*" + +[package.extras] +dev = ["environs[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +tests = ["environs[django]", "pytest"] + [[package]] name = "fastapi" -version = "0.115.0" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.0-py3-none-any.whl", hash = "sha256:17ea427674467486e997206a5ab25760f6b09e069f099b96f5b55a32fb6f1631"}, - {file = "fastapi-0.115.0.tar.gz", hash = "sha256:f93b4ca3529a8ebc6fc3fcf710e5efa8de3df9b41570958abf1d97d843138004"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.40.0,<0.42.0" typing-extensions = ">=4.8.0" [package.extras] all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] +[[package]] +name = "feedparser" +version = "6.0.11" +description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds" +optional = false +python-versions = ">=3.6" +files = [ + {file = "feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45"}, + {file = "feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5"}, +] + +[package.dependencies] +sgmllib3k = "*" + +[[package]] +name = "filetype" +version = "1.2.0" +description = "Infer file type and MIME type of any file/buffer. No external dependencies." +optional = false +python-versions = "*" +files = [ + {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, + {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, +] + [[package]] name = "fire" version = "0.6.0" @@ -502,88 +876,153 @@ termcolor = "*" [[package]] name = "frozenlist" -version = "1.4.1" +version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + +[[package]] +name = "fsspec" +version = "2024.10.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"}, + {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "genson" +version = "1.3.0" +description = "GenSON is a powerful, user-friendly JSON Schema generator." +optional = false +python-versions = "*" +files = [ + {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"}, + {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, ] [[package]] @@ -599,75 +1038,111 @@ files = [ [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "gunicorn" +version = "23.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "h11" version = "0.14.0" @@ -681,13 +1156,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -725,6 +1200,17 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "httpx-sse" +version = "0.4.0" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, + {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, +] + [[package]] name = "idna" version = "3.10" @@ -741,13 +1227,13 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "immutabledict" -version = "4.2.0" +version = "4.2.1" description = "Immutable wrapper around dictionaries (a fork of frozendict)" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.8" files = [ - {file = "immutabledict-4.2.0-py3-none-any.whl", hash = "sha256:d728b2c2410d698d95e6200237feb50a695584d20289ad3379a439aa3d90baba"}, - {file = "immutabledict-4.2.0.tar.gz", hash = "sha256:e003fd81aad2377a5a758bf7e1086cf3b70b63e9a5cc2f46bce8d0a2b4727c5f"}, + {file = "immutabledict-4.2.1-py3-none-any.whl", hash = "sha256:c56a26ced38c236f79e74af3ccce53772827cef5c3bce7cab33ff2060f756373"}, + {file = "immutabledict-4.2.1.tar.gz", hash = "sha256:d91017248981c72eb66c8ff9834e99c2f53562346f23e7f51e7a5ebcf66a3bcc"}, ] [[package]] @@ -764,6 +1250,21 @@ files = [ [package.dependencies] networkx = ">=2" +[[package]] +name = "inflect" +version = "5.6.2" +description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words" +optional = false +python-versions = ">=3.7" +files = [ + {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"}, + {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"}, +] + +[package.extras] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] +testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -775,6 +1276,20 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + [[package]] name = "jinja2" version = "3.1.4" @@ -794,72 +1309,97 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.5.0" +version = "0.8.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" files = [ - {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, - {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc0d5b8b3dd12e91dd184b87273f864b363dfabc90ef29a1092d269f18c7e28"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c22541f0b672f4d741382a97c65609332a783501551445ab2df137ada01e019e"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63314832e302cc10d8dfbda0333a384bf4bcfce80d65fe99b0f3c0da8945a91a"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a25fbd8a5a58061e433d6fae6d5298777c0814a8bcefa1e5ecfff20c594bd749"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:503b2c27d87dfff5ab717a8200fbbcf4714516c9d85558048b1fc14d2de7d8dc"}, - {file = "jiter-0.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d1f3d27cce923713933a844872d213d244e09b53ec99b7a7fdf73d543529d6d"}, - {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c95980207b3998f2c3b3098f357994d3fd7661121f30669ca7cb945f09510a87"}, - {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afa66939d834b0ce063f57d9895e8036ffc41c4bd90e4a99631e5f261d9b518e"}, - {file = "jiter-0.5.0-cp310-none-win32.whl", hash = "sha256:f16ca8f10e62f25fd81d5310e852df6649af17824146ca74647a018424ddeccf"}, - {file = "jiter-0.5.0-cp310-none-win_amd64.whl", hash = "sha256:b2950e4798e82dd9176935ef6a55cf6a448b5c71515a556da3f6b811a7844f1e"}, - {file = "jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553"}, - {file = "jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e"}, - {file = "jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06"}, - {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403"}, - {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646"}, - {file = "jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb"}, - {file = "jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae"}, - {file = "jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a"}, - {file = "jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e"}, - {file = "jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a"}, - {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e"}, - {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338"}, - {file = "jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4"}, - {file = "jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5"}, - {file = "jiter-0.5.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f04bc2fc50dc77be9d10f73fcc4e39346402ffe21726ff41028f36e179b587e6"}, - {file = "jiter-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f433a4169ad22fcb550b11179bb2b4fd405de9b982601914ef448390b2954f3"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad4a6398c85d3a20067e6c69890ca01f68659da94d74c800298581724e426c7e"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6baa88334e7af3f4d7a5c66c3a63808e5efbc3698a1c57626541ddd22f8e4fbf"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ece0a115c05efca597c6d938f88c9357c843f8c245dbbb53361a1c01afd7148"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:335942557162ad372cc367ffaf93217117401bf930483b4b3ebdb1223dbddfa7"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649b0ee97a6e6da174bffcb3c8c051a5935d7d4f2f52ea1583b5b3e7822fbf14"}, - {file = "jiter-0.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4be354c5de82157886ca7f5925dbda369b77344b4b4adf2723079715f823989"}, - {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5206144578831a6de278a38896864ded4ed96af66e1e63ec5dd7f4a1fce38a3a"}, - {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8120c60f8121ac3d6f072b97ef0e71770cc72b3c23084c72c4189428b1b1d3b6"}, - {file = "jiter-0.5.0-cp38-none-win32.whl", hash = "sha256:6f1223f88b6d76b519cb033a4d3687ca157c272ec5d6015c322fc5b3074d8a5e"}, - {file = "jiter-0.5.0-cp38-none-win_amd64.whl", hash = "sha256:c59614b225d9f434ea8fc0d0bec51ef5fa8c83679afedc0433905994fb36d631"}, - {file = "jiter-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0af3838cfb7e6afee3f00dc66fa24695199e20ba87df26e942820345b0afc566"}, - {file = "jiter-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:550b11d669600dbc342364fd4adbe987f14d0bbedaf06feb1b983383dcc4b961"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489875bf1a0ffb3cb38a727b01e6673f0f2e395b2aad3c9387f94187cb214bbf"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b250ca2594f5599ca82ba7e68785a669b352156260c5362ea1b4e04a0f3e2389"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ea18e01f785c6667ca15407cd6dabbe029d77474d53595a189bdc813347218e"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462a52be85b53cd9bffd94e2d788a09984274fe6cebb893d6287e1c296d50653"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92cc68b48d50fa472c79c93965e19bd48f40f207cb557a8346daa020d6ba973b"}, - {file = "jiter-0.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c834133e59a8521bc87ebcad773608c6fa6ab5c7a022df24a45030826cf10bc"}, - {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab3a71ff31cf2d45cb216dc37af522d335211f3a972d2fe14ea99073de6cb104"}, - {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cccd3af9c48ac500c95e1bcbc498020c87e1781ff0345dd371462d67b76643eb"}, - {file = "jiter-0.5.0-cp39-none-win32.whl", hash = "sha256:368084d8d5c4fc40ff7c3cc513c4f73e02c85f6009217922d0823a48ee7adf61"}, - {file = "jiter-0.5.0-cp39-none-win_amd64.whl", hash = "sha256:ce03f7b4129eb72f1687fa11300fbf677b02990618428934662406d2a76742a1"}, - {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, + {file = "jiter-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:dee4eeb293ffcd2c3b31ebab684dbf7f7b71fe198f8eddcdf3a042cc6e10205a"}, + {file = "jiter-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aad1e6e9b01cf0304dcee14db03e92e0073287a6297caf5caf2e9dbfea16a924"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:504099fb7acdbe763e10690d560a25d4aee03d918d6a063f3a761d8a09fb833f"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2373487caad7fe39581f588ab5c9262fc1ade078d448626fec93f4ffba528858"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c341ecc3f9bccde952898b0c97c24f75b84b56a7e2f8bbc7c8e38cab0875a027"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e48e7a336529b9419d299b70c358d4ebf99b8f4b847ed3f1000ec9f320e8c0c"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ee157a8afd2943be690db679f82fafb8d347a8342e8b9c34863de30c538d55"}, + {file = "jiter-0.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7dceae3549b80087f913aad4acc2a7c1e0ab7cb983effd78bdc9c41cabdcf18"}, + {file = "jiter-0.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e29e9ecce53d396772590438214cac4ab89776f5e60bd30601f1050b34464019"}, + {file = "jiter-0.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fa1782f22d5f92c620153133f35a9a395d3f3823374bceddd3e7032e2fdfa0b1"}, + {file = "jiter-0.8.0-cp310-none-win32.whl", hash = "sha256:f754ef13b4e4f67a3bf59fe974ef4342523801c48bf422f720bd37a02a360584"}, + {file = "jiter-0.8.0-cp310-none-win_amd64.whl", hash = "sha256:796f750b65f5d605f5e7acaccc6b051675e60c41d7ac3eab40dbd7b5b81a290f"}, + {file = "jiter-0.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f6f4e645efd96b4690b9b6091dbd4e0fa2885ba5c57a0305c1916b75b4f30ff6"}, + {file = "jiter-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f61cf6d93c1ade9b8245c9f14b7900feadb0b7899dbe4aa8de268b705647df81"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0396bc5cb1309c6dab085e70bb3913cdd92218315e47b44afe9eace68ee8adaa"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62d0e42ec5dc772bd8554a304358220be5d97d721c4648b23f3a9c01ccc2cb26"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec4b711989860705733fc59fb8c41b2def97041cea656b37cf6c8ea8dee1c3f4"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859cc35bf304ab066d88f10a44a3251a9cd057fb11ec23e00be22206db878f4f"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5000195921aa293b39b9b5bc959d7fa658e7f18f938c0e52732da8e3cc70a278"}, + {file = "jiter-0.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36050284c0abde57aba34964d3920f3d6228211b65df7187059bb7c7f143759a"}, + {file = "jiter-0.8.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a88f608e050cfe45c48d771e86ecdbf5258314c883c986d4217cc79e1fb5f689"}, + {file = "jiter-0.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:646cf4237665b2e13b4159d8f26d53f59bc9f2e6e135e3a508a2e5dd26d978c6"}, + {file = "jiter-0.8.0-cp311-none-win32.whl", hash = "sha256:21fe5b8345db1b3023052b2ade9bb4d369417827242892051244af8fae8ba231"}, + {file = "jiter-0.8.0-cp311-none-win_amd64.whl", hash = "sha256:30c2161c5493acf6b6c3c909973fb64ae863747def01cc7574f3954e0a15042c"}, + {file = "jiter-0.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d91a52d8f49ada2672a4b808a0c5c25d28f320a2c9ca690e30ebd561eb5a1002"}, + {file = "jiter-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c38cf25cf7862f61410b7a49684d34eb3b5bcbd7ddaf4773eea40e0bd43de706"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6189beb5c4b3117624be6b2e84545cff7611f5855d02de2d06ff68e316182be"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13fa849c0e30643554add089983caa82f027d69fad8f50acadcb21c462244ab"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7765ca159d0a58e8e0f8ca972cd6d26a33bc97b4480d0d2309856763807cd28"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b0befe7c6e9fc867d5bed21bab0131dfe27d1fa5cd52ba2bced67da33730b7d"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d6363d4c6f1052b1d8b494eb9a72667c3ef5f80ebacfe18712728e85327000"}, + {file = "jiter-0.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a873e57009863eeac3e3969e4653f07031d6270d037d6224415074ac17e5505c"}, + {file = "jiter-0.8.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2582912473c0d9940791479fe1bf2976a34f212eb8e0a82ee9e645ac275c5d16"}, + {file = "jiter-0.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:646163201af42f55393ee6e8f6136b8df488253a6533f4230a64242ecbfe6048"}, + {file = "jiter-0.8.0-cp312-none-win32.whl", hash = "sha256:96e75c9abfbf7387cba89a324d2356d86d8897ac58c956017d062ad510832dae"}, + {file = "jiter-0.8.0-cp312-none-win_amd64.whl", hash = "sha256:ed6074552b4a32e047b52dad5ab497223721efbd0e9efe68c67749f094a092f7"}, + {file = "jiter-0.8.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:dd5e351cb9b3e676ec3360a85ea96def515ad2b83c8ae3a251ce84985a2c9a6f"}, + {file = "jiter-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba9f12b0f801ecd5ed0cec29041dc425d1050922b434314c592fc30d51022467"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7ba461c3681728d556392e8ae56fb44a550155a24905f01982317b367c21dd4"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a15ed47ab09576db560dbc5c2c5a64477535beb056cd7d997d5dd0f2798770e"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cef55042816d0737142b0ec056c0356a5f681fb8d6aa8499b158e87098f4c6f8"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:549f170215adeb5e866f10617c3d019d8eb4e6d4e3c6b724b3b8c056514a3487"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f867edeb279d22020877640d2ea728de5817378c60a51be8af731a8a8f525306"}, + {file = "jiter-0.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aef8845f463093799db4464cee2aa59d61aa8edcb3762aaa4aacbec3f478c929"}, + {file = "jiter-0.8.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:d0d6e22e4062c3d3c1bf3594baa2f67fc9dcdda8275abad99e468e0c6540bc54"}, + {file = "jiter-0.8.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:079e62e64696241ac3f408e337aaac09137ed760ccf2b72b1094b48745c13641"}, + {file = "jiter-0.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74d2b56ed3da5760544df53b5f5c39782e68efb64dc3aa0bba4cc08815e6fae8"}, + {file = "jiter-0.8.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:798dafe108cba58a7bb0a50d4d5971f98bb7f3c974e1373e750de6eb21c1a329"}, + {file = "jiter-0.8.0-cp313-none-win32.whl", hash = "sha256:ca6d3064dfc743eb0d3d7539d89d4ba886957c717567adc72744341c1e3573c9"}, + {file = "jiter-0.8.0-cp313-none-win_amd64.whl", hash = "sha256:38caedda64fe1f04b06d7011fc15e86b3b837ed5088657bf778656551e3cd8f9"}, + {file = "jiter-0.8.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:bb5c8a0a8d081c338db22e5b8d53a89a121790569cbb85f7d3cfb1fe0fbe9836"}, + {file = "jiter-0.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:202dbe8970bfb166fab950eaab8f829c505730a0b33cc5e1cfb0a1c9dd56b2f9"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9046812e5671fdcfb9ae02881fff1f6a14d484b7e8b3316179a372cdfa1e8026"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e6ac56425023e52d65150918ae25480d0a1ce2a6bf5ea2097f66a2cc50f6d692"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dfcf97210c6eab9d2a1c6af15dd39e1d5154b96a7145d0a97fa1df865b7b834"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4e3c8444d418686f78c9a547b9b90031faf72a0a1a46bfec7fb31edbd889c0d"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6507011a299b7f578559084256405a8428875540d8d13530e00b688e41b09493"}, + {file = "jiter-0.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0aae4738eafdd34f0f25c2d3668ce9e8fa0d7cb75a2efae543c9a69aebc37323"}, + {file = "jiter-0.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5d782e790396b13f2a7b36bdcaa3736a33293bdda80a4bf1a3ce0cd5ef9f15"}, + {file = "jiter-0.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc7f993bc2c4e03015445adbb16790c303282fce2e8d9dc3a3905b1d40e50564"}, + {file = "jiter-0.8.0-cp38-none-win32.whl", hash = "sha256:d4a8a6eda018a991fa58ef707dd51524055d11f5acb2f516d70b1be1d15ab39c"}, + {file = "jiter-0.8.0-cp38-none-win_amd64.whl", hash = "sha256:4cca948a3eda8ea24ed98acb0ee19dc755b6ad2e570ec85e1527d5167f91ff67"}, + {file = "jiter-0.8.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ef89663678d8257063ce7c00d94638e05bd72f662c5e1eb0e07a172e6c1a9a9f"}, + {file = "jiter-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c402ddcba90b4cc71db3216e8330f4db36e0da2c78cf1d8a9c3ed8f272602a94"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a6dfe795b7a173a9f8ba7421cdd92193d60c1c973bbc50dc3758a9ad0fa5eb6"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ec29a31b9abd6be39453a2c45da067138a3005d65d2c0507c530e0f1fdcd9a4"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a488f8c54bddc3ddefaf3bfd6de4a52c97fc265d77bc2dcc6ee540c17e8c342"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeb5561adf4d26ca0d01b5811b4d7b56a8986699a473d700757b4758ef787883"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab961858d7ad13132328517d29f121ae1b2d94502191d6bcf96bddcc8bb5d1c"}, + {file = "jiter-0.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a207e718d114d23acf0850a2174d290f42763d955030d9924ffa4227dbd0018f"}, + {file = "jiter-0.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:733bc9dc8ff718a0ae4695239e9268eb93e88b73b367dfac3ec227d8ce2f1e77"}, + {file = "jiter-0.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1ec27299e22d05e13a06e460bf7f75f26f9aaa0e0fb7d060f40e88df1d81faa"}, + {file = "jiter-0.8.0-cp39-none-win32.whl", hash = "sha256:e8dbfcb46553e6661d3fc1f33831598fcddf73d0f67834bce9fc3e9ebfe5c439"}, + {file = "jiter-0.8.0-cp39-none-win_amd64.whl", hash = "sha256:af2ce2487b3a93747e2cb5150081d4ae1e5874fce5924fc1a12e9e768e489ad8"}, + {file = "jiter-0.8.0.tar.gz", hash = "sha256:86fee98b569d4cc511ff2e3ec131354fafebd9348a487549c31ad371ae730310"}, +] + +[[package]] +name = "joblib" +version = "1.4.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, ] [[package]] @@ -889,60 +1429,61 @@ files = [ [[package]] name = "langchain" -version = "0.3.2" +version = "0.3.8" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain-0.3.2-py3-none-any.whl", hash = "sha256:cf005dcba132e46fb5e8d3dfaf7f8751bffd2d73e738c36be58f41edc7e3a4b8"}, - {file = "langchain-0.3.2.tar.gz", hash = "sha256:dc330e6eb10d81d23ba0305d18358702c73cc59e95c410eca6c6779aab4ddc9b"}, + {file = "langchain-0.3.8-py3-none-any.whl", hash = "sha256:5cae404da30bf6730639a9ad85d3bf4fbb350c0038e5a0b81890e5883b4cff5c"}, + {file = "langchain-0.3.8.tar.gz", hash = "sha256:1cbbf7379b5b2f11b751fc527016f29ee5fe8a2697d166b52b7b5c63fc9702f9"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" -langchain-core = ">=0.3.8,<0.4.0" +langchain-core = ">=0.3.21,<0.4.0" langchain-text-splitters = ">=0.3.0,<0.4.0" langsmith = ">=0.1.17,<0.2.0" -numpy = {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""} +numpy = {version = ">=1.26.2,<2", markers = "python_version >= \"3.12\""} pydantic = ">=2.7.4,<3.0.0" PyYAML = ">=5.3" requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10" [[package]] name = "langchain-community" -version = "0.3.1" +version = "0.3.4" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain_community-0.3.1-py3-none-any.whl", hash = "sha256:627eb26c16417764762ac47dd0d3005109f750f40242a88bb8f2958b798bcf90"}, - {file = "langchain_community-0.3.1.tar.gz", hash = "sha256:c964a70628f266a61647e58f2f0434db633d4287a729f100a81dd8b0654aec93"}, + {file = "langchain_community-0.3.4-py3-none-any.whl", hash = "sha256:67a44d3db8ba14a8abae67c8f611e6dc20002446439e761f673c7dffa506fb85"}, + {file = "langchain_community-0.3.4.tar.gz", hash = "sha256:80c7e6491788449b8a6e7a31444ff8ebb5c32242f67a65aa33d56ad35a7b5b5c"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain = ">=0.3.1,<0.4.0" -langchain-core = ">=0.3.6,<0.4.0" +httpx-sse = ">=0.4.0,<0.5.0" +langchain = ">=0.3.6,<0.4.0" +langchain-core = ">=0.3.14,<0.4.0" langsmith = ">=0.1.125,<0.2.0" numpy = {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""} pydantic-settings = ">=2.4.0,<3.0.0" PyYAML = ">=5.3" requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10" [[package]] name = "langchain-core" -version = "0.3.9" +version = "0.3.21" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain_core-0.3.9-py3-none-any.whl", hash = "sha256:26efa048666c7de56d0ab311de2c0778b04cbb2ffe95bff76139118f13815d01"}, - {file = "langchain_core-0.3.9.tar.gz", hash = "sha256:7a6ac988d24d0ddce5874b28f538cd95f69f502b7f50581de22aca0dc58199a8"}, + {file = "langchain_core-0.3.21-py3-none-any.whl", hash = "sha256:7e723dff80946a1198976c6876fea8326dc82566ef9bcb5f8d9188f738733665"}, + {file = "langchain_core-0.3.21.tar.gz", hash = "sha256:561b52b258ffa50a9fb11d7a1940ebfd915654d1ec95b35e81dfd5ee84143411"}, ] [package.dependencies] @@ -954,37 +1495,37 @@ pydantic = [ {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, ] PyYAML = ">=5.3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0" typing-extensions = ">=4.7" [[package]] name = "langchain-text-splitters" -version = "0.3.0" +version = "0.3.2" description = "LangChain text splitting utilities" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain_text_splitters-0.3.0-py3-none-any.whl", hash = "sha256:e84243e45eaff16e5b776cd9c81b6d07c55c010ebcb1965deb3d1792b7358e83"}, - {file = "langchain_text_splitters-0.3.0.tar.gz", hash = "sha256:f9fe0b4d244db1d6de211e7343d4abc4aa90295aa22e1f0c89e51f33c55cd7ce"}, + {file = "langchain_text_splitters-0.3.2-py3-none-any.whl", hash = "sha256:0db28c53f41d1bc024cdb3b1646741f6d46d5371e90f31e7e7c9fbe75d01c726"}, + {file = "langchain_text_splitters-0.3.2.tar.gz", hash = "sha256:81e6515d9901d6dd8e35fb31ccd4f30f76d44b771890c789dc835ef9f16204df"}, ] [package.dependencies] -langchain-core = ">=0.3.0,<0.4.0" +langchain-core = ">=0.3.15,<0.4.0" [[package]] name = "langsmith" -version = "0.1.131" +version = "0.1.146" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.131-py3-none-any.whl", hash = "sha256:80c106b1c42307195cc0bb3a596472c41ef91b79d15bcee9938307800336c563"}, - {file = "langsmith-0.1.131.tar.gz", hash = "sha256:626101a3bf3ca481e5110d5155ace8aa066e4e9cc2fa7d96c8290ade0fbff797"}, + {file = "langsmith-0.1.146-py3-none-any.whl", hash = "sha256:9d062222f1a32c9b047dab0149b24958f988989cd8d4a5f9139ff959a51e59d8"}, + {file = "langsmith-0.1.146.tar.gz", hash = "sha256:ead8b0b9d5b6cd3ac42937ec48bdf09d4afe7ca1bba22dc05eb65591a18106f8"}, ] [package.dependencies] httpx = ">=0.23.0,<1" -orjson = ">=3.9.14,<4.0.0" +orjson = {version = ">=3.9.14,<4.0.0", markers = "platform_python_implementation != \"PyPy\""} pydantic = [ {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, @@ -993,132 +1534,440 @@ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" [[package]] -name = "libcst" -version = "1.4.0" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." +name = "libcst" +version = "1.5.1" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "libcst-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab83633e61ee91df575a3838b1e73c371f19d4916bf1816554933235553d41ea"}, + {file = "libcst-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b58a49895d95ec1fd34fad041a142d98edf9b51fcaf632337c13befeb4d51c7c"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9ec764aa781ef35ab96b693569ac3dced16df9feb40ee6c274d13e86a1472e"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99bbffd8596d192bc0e844a4cf3c4fc696979d4e20ab1c0774a01768a59b47ed"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec6ee607cfe4cc4cc93e56e0188fdb9e50399d61a1262d58229752946f288f5e"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72132756f985a19ef64d702a821099d4afc3544974662772b44cbc55b7279727"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:40b75bf2d70fc0bc26b1fa73e61bdc46fef59f5c71aedf16128e7c33db8d5e40"}, + {file = "libcst-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:56c944acaa781b8e586df3019374f5cf117054d7fc98f85be1ba84fe810005dc"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db7711a762b0327b581be5a963908fecd74412bdda34db34553faa521563c22d"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa524bd012aaae1f485fd44490ef5abf708b14d2addc0f06b28de3e4585c4b9e"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffb8135c09e41e8cf710b152c33e9b7f1d0d0b9f242bae0c502eb082fdb1fb"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76a8ac7a84f9b6f678a668bff85b360e0a93fa8d7f25a74a206a28110734bb2a"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89c808bdb5fa9ca02df41dd234cbb0e9de0d2e0c029c7063d5435a9f6781cc10"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40fbbaa8b839bfbfa5b300623ca2b6b0768b58bbc31b341afbc99110c9bee232"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c7021e3904d8d088c369afc3fe17c279883e583415ef07edacadba76cfbecd27"}, + {file = "libcst-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f053a5deb6a214972dbe9fa26ecd8255edb903de084a3d7715bf9e9da8821c50"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:666813950b8637af0c0e96b1ca46f5d5f183d2fe50bbac2186f5b283a99f3529"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b58b36022ae77a5a00002854043ae95c03e92f6062ad08473eff326f32efa0"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb13d7c598fe9a798a1d22eae56ab3d3d599b38b83436039bd6ae229fc854d7"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5987daff8389b0df60b5c20499ff4fb73fc03cb3ae1f6a746eefd204ed08df85"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f3d2f32ee081bad3394546b0b9ac5e31686d3b5cfe4892d716d2ba65f9ec08"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ff21005c33b634957a98db438e882522febf1cacc62fa716f29e163a3f5871a"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:15697ea9f1edbb9a263364d966c72abda07195d1c1a6838eb79af057f1040770"}, + {file = "libcst-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:cedd4c8336e01c51913113fbf5566b8f61a86d90f3d5cc5b1cb5049575622c5f"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:06a9b4c9b76da4a7399e6f1f3a325196fb5febd3ea59fac1f68e2116f3517cd8"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:940ec4c8db4c2d620a7268d6c83e64ff646e4afd74ae5183d0f0ef3b80e05be0"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbccb016b1ac6d892344300dcccc8a16887b71bb7f875ba56c0ed6c1a7ade8be"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c615af2117320e9a218083c83ec61227d3547e38a0de80329376971765f27a9e"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02b38fa4d9f13e79fe69e9b5407b9e173557bcfb5960f7866cf4145af9c7ae09"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3334afe9e7270e175de01198f816b0dc78dda94d9d72152b61851c323e4e741e"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26c804fa8091747128579013df0b5f8e6b0c7904d9c4ee83841f136f53e18684"}, + {file = "libcst-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:b5a0d3c632aa2b21c5fa145e4e8dbf86f45c9b37a64c0b7221a5a45caf58915a"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1cc7393aaac733e963f0ee00466d059db74a38e15fc7e6a46dddd128c5be8d08"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bbaf5755be50fa9b35a3d553d1e62293fbb2ee5ce2c16c7e7ffeb2746af1ab88"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e397f5b6c0fc271acea44579f154b0f3ab36011050f6db75ab00cef47441946"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1947790a4fd7d96bcc200a6ecaa528045fcb26a34a24030d5859c7983662289e"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:697eabe9f5ffc40f76d6d02e693274e0a382826d0cf8183bd44e7407dfb0ab90"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dc06b7c60d086ef1832aebfd31b64c3c8a645adf0c5638d6243e5838f6a9356e"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19e39cfef4316599ca20d1c821490aeb783b52e8a8543a824972a525322a85d0"}, + {file = "libcst-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:01e01c04f0641188160d3b99c6526436e93a3fbf9783dba970f9885a77ec9b38"}, + {file = "libcst-1.5.1.tar.gz", hash = "sha256:71cb294db84df9e410208009c732628e920111683c2f2b2e0c5b71b98464f365"}, +] + +[package.dependencies] +pyyaml = ">=5.2" + +[package.extras] +dev = ["Sphinx (>=5.1.1)", "black (==24.8.0)", "build (>=0.10.0)", "coverage[toml] (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.1.1)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=1.7.0,<1.8)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.7.3)", "usort (==1.0.8.post1)"] + +[[package]] +name = "llama-cloud" +version = "0.1.5" +description = "" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "llama_cloud-0.1.5-py3-none-any.whl", hash = "sha256:15605022520d04bd6ef6a46c0cbde833f301d652286d34fca02b4c44e2a7a2aa"}, + {file = "llama_cloud-0.1.5.tar.gz", hash = "sha256:8ce1db36754a6a46c8511561dbc040a2e89ba4ca1cf4edfb6ce382a5240f6cb6"}, +] + +[package.dependencies] +httpx = ">=0.20.0" +pydantic = ">=1.10" + +[[package]] +name = "llama-index" +version = "0.11.23" +description = "Interface between LLMs and your data" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index-0.11.23-py3-none-any.whl", hash = "sha256:08513b86fed8e4295b52dc204c193e7186a2e65fc09d581deeb6a74afaaf74fe"}, + {file = "llama_index-0.11.23.tar.gz", hash = "sha256:e02118dfe060568cf40c9ed109c16d559911b2e19eac5e6babc04ed6b57ede02"}, +] + +[package.dependencies] +llama-index-agent-openai = ">=0.3.4,<0.4.0" +llama-index-cli = ">=0.3.1,<0.4.0" +llama-index-core = ">=0.11.23,<0.12.0" +llama-index-embeddings-openai = ">=0.2.4,<0.3.0" +llama-index-indices-managed-llama-cloud = ">=0.3.0" +llama-index-legacy = ">=0.9.48,<0.10.0" +llama-index-llms-openai = ">=0.2.10,<0.3.0" +llama-index-multi-modal-llms-openai = ">=0.2.0,<0.3.0" +llama-index-program-openai = ">=0.2.0,<0.3.0" +llama-index-question-gen-openai = ">=0.2.0,<0.3.0" +llama-index-readers-file = ">=0.3.0,<0.4.0" +llama-index-readers-llama-parse = ">=0.3.0" +nltk = ">3.8.1" + +[[package]] +name = "llama-index-agent-openai" +version = "0.3.4" +description = "llama-index agent openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_agent_openai-0.3.4-py3-none-any.whl", hash = "sha256:3720ce9bb12417a99a3fe84e52cce23e762b13f88a2dfc4292c76f4df9b26b4a"}, + {file = "llama_index_agent_openai-0.3.4.tar.gz", hash = "sha256:80e3408d97121bebca3fa3ffd14b51285870c1c3c73d4ee04d3d18cfe6040466"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +llama-index-llms-openai = ">=0.2.9,<0.3.0" +openai = ">=1.14.0" + +[[package]] +name = "llama-index-cli" +version = "0.3.1" +description = "llama-index cli" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_cli-0.3.1-py3-none-any.whl", hash = "sha256:2111fbb6973f5b1eabce0d6cca3986499f0f2f625b13d7f48269a49c64c027d4"}, + {file = "llama_index_cli-0.3.1.tar.gz", hash = "sha256:1890dd687cf440f3651365a549e303363162c167b8efbd87a3aa10058d6d5c77"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +llama-index-embeddings-openai = ">=0.2.0,<0.3.0" +llama-index-llms-openai = ">=0.2.0,<0.3.0" + +[[package]] +name = "llama-index-core" +version = "0.11.23" +description = "Interface between LLMs and your data" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_core-0.11.23-py3-none-any.whl", hash = "sha256:25a0cb4a055bfb348655ca4acd1b475529bd8537a7b81874ef14ed13f56e06c1"}, + {file = "llama_index_core-0.11.23.tar.gz", hash = "sha256:e150859696a0eae169fe19323f46e9a31af2c12c3182012e4d0353ea8eb06d24"}, +] + +[package.dependencies] +aiohttp = ">=3.8.6,<4.0.0" +dataclasses-json = "*" +deprecated = ">=1.2.9.3" +dirtyjson = ">=1.0.8,<2.0.0" +filetype = ">=1.2.0,<2.0.0" +fsspec = ">=2023.5.0" +httpx = "*" +nest-asyncio = ">=1.5.8,<2.0.0" +networkx = ">=3.0" +nltk = ">3.8.1" +numpy = "<2.0.0" +pillow = ">=9.0.0" +pydantic = ">=2.7.0,<3.0.0" +PyYAML = ">=6.0.1" +requests = ">=2.31.0" +SQLAlchemy = {version = ">=1.4.49", extras = ["asyncio"]} +tenacity = ">=8.2.0,<8.4.0 || >8.4.0,<9.0.0" +tiktoken = ">=0.3.3" +tqdm = ">=4.66.1,<5.0.0" +typing-extensions = ">=4.5.0" +typing-inspect = ">=0.8.0" +wrapt = "*" + +[[package]] +name = "llama-index-embeddings-openai" +version = "0.2.5" +description = "llama-index embeddings openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_embeddings_openai-0.2.5-py3-none-any.whl", hash = "sha256:823c8311e556349ba19dda408a64a314fa3dafe0e5759709c54d33a0269aa6ba"}, + {file = "llama_index_embeddings_openai-0.2.5.tar.gz", hash = "sha256:0047dd71d747068645ed728c29312aa91b65bbe4c6142180034c64dfc5c6f6e8"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +openai = ">=1.1.0" + +[[package]] +name = "llama-index-indices-managed-llama-cloud" +version = "0.6.0" +description = "llama-index indices llama-cloud integration" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "llama_index_indices_managed_llama_cloud-0.6.0-py3-none-any.whl", hash = "sha256:18a3bbb386c4fbda8883cf40339bde402637e4cd5e06bcf3870d8c174b9baa3a"}, + {file = "llama_index_indices_managed_llama_cloud-0.6.0.tar.gz", hash = "sha256:fe32aecb87ffd81eb824fc64509cc991c3cde574455e53e73a4dbe30961c4f21"}, +] + +[package.dependencies] +llama-cloud = ">=0.1.5" +llama-index-core = ">=0.11.13.post1,<0.12.0" + +[[package]] +name = "llama-index-legacy" +version = "0.9.48.post4" +description = "Interface between LLMs and your data" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_legacy-0.9.48.post4-py3-none-any.whl", hash = "sha256:4b817d7c343fb5f7f00c4410eff519f320013b8d5f24c4fedcf270c471f92038"}, + {file = "llama_index_legacy-0.9.48.post4.tar.gz", hash = "sha256:f8a9764e7e134a52bfef5e53d2d62561bfc01fc09874c51cc001df6f5302ae30"}, +] + +[package.dependencies] +aiohttp = ">=3.8.6,<4.0.0" +dataclasses-json = "*" +deprecated = ">=1.2.9.3" +dirtyjson = ">=1.0.8,<2.0.0" +fsspec = ">=2023.5.0" +httpx = "*" +nest-asyncio = ">=1.5.8,<2.0.0" +networkx = ">=3.0" +nltk = ">=3.8.1" +numpy = "*" +openai = ">=1.1.0" +pandas = "*" +requests = ">=2.31.0" +SQLAlchemy = {version = ">=1.4.49", extras = ["asyncio"]} +tenacity = ">=8.2.0,<9.0.0" +tiktoken = ">=0.3.3" +typing-extensions = ">=4.5.0" +typing-inspect = ">=0.8.0" + +[package.extras] +gradientai = ["gradientai (>=1.4.0)"] +html = ["beautifulsoup4 (>=4.12.2,<5.0.0)"] +langchain = ["langchain (>=0.0.303)"] +local-models = ["optimum[onnxruntime] (>=1.13.2,<2.0.0)", "sentencepiece (>=0.1.99,<0.2.0)", "transformers[torch] (>=4.33.1,<5.0.0)"] +postgres = ["asyncpg (>=0.28.0,<0.29.0)", "pgvector (>=0.1.0,<0.2.0)", "psycopg2-binary (>=2.9.9,<3.0.0)"] +query-tools = ["guidance (>=0.0.64,<0.0.65)", "jsonpath-ng (>=1.6.0,<2.0.0)", "lm-format-enforcer (>=0.4.3,<0.5.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "scikit-learn", "spacy (>=3.7.1,<4.0.0)"] + +[[package]] +name = "llama-index-llms-openai" +version = "0.2.16" +description = "llama-index llms openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_llms_openai-0.2.16-py3-none-any.whl", hash = "sha256:413466acbb894bd81f8dab2037f595e92392d869eec6d8274a16d43123cac8b6"}, + {file = "llama_index_llms_openai-0.2.16.tar.gz", hash = "sha256:7c666dd27056c278a079ff45d53f1fbfc8ed363764aa7baeee2e03df47f9072a"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.7,<0.12.0" +openai = ">=1.40.0,<2.0.0" + +[[package]] +name = "llama-index-multi-modal-llms-openai" +version = "0.2.3" +description = "llama-index multi-modal-llms openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_multi_modal_llms_openai-0.2.3-py3-none-any.whl", hash = "sha256:96b36beb2c3fca4faca80c59ecf7c6c6629ecdb96c288ef89777b592ec43f872"}, + {file = "llama_index_multi_modal_llms_openai-0.2.3.tar.gz", hash = "sha256:8eb9b7f1ff3956ef0979e21bc83e6a885e40987b7199f195e46525d06e3ae402"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +llama-index-llms-openai = ">=0.2.11,<0.3.0" + +[[package]] +name = "llama-index-program-openai" +version = "0.2.0" +description = "llama-index program openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_program_openai-0.2.0-py3-none-any.whl", hash = "sha256:2e10d0c8f21af2e9443eb79e81bb31e7b73835b7c7bbd7ddf20e0a9c846cd368"}, + {file = "llama_index_program_openai-0.2.0.tar.gz", hash = "sha256:4139935541c011257fbfeb9662b3bf1237b729ef4b1c8f4ddf5b6789d2374ac4"}, +] + +[package.dependencies] +llama-index-agent-openai = ">=0.3.0,<0.4.0" +llama-index-core = ">=0.11.0,<0.12.0" +llama-index-llms-openai = ">=0.2.0,<0.3.0" + +[[package]] +name = "llama-index-question-gen-openai" +version = "0.2.0" +description = "llama-index question_gen openai integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_question_gen_openai-0.2.0-py3-none-any.whl", hash = "sha256:a16e68fc5434e9a793f1dfd0cc0354ee19afd167f1d499403b0085b11c5406c0"}, + {file = "llama_index_question_gen_openai-0.2.0.tar.gz", hash = "sha256:3dde1cecbd651000639c20031d7ea23334276aabb181cac40ff424f35e10465e"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +llama-index-llms-openai = ">=0.2.0,<0.3.0" +llama-index-program-openai = ">=0.2.0,<0.3.0" + +[[package]] +name = "llama-index-readers-file" +version = "0.3.0" +description = "llama-index readers file integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_readers_file-0.3.0-py3-none-any.whl", hash = "sha256:259ee195ea8285f8421b381ec3a64f5fae68de69435970ae64e544f07a4403ad"}, + {file = "llama_index_readers_file-0.3.0.tar.gz", hash = "sha256:6c675fcd2f0599a131f89e1c5ed3521dde31338a9b724a7721f5dfd7243ea8d8"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.12.3,<5.0.0" +llama-index-core = ">=0.11.0,<0.12.0" +pandas = "*" +pypdf = ">=5.1.0,<6.0.0" +striprtf = ">=0.0.26,<0.0.27" + +[package.extras] +pymupdf = ["pymupdf (>=1.23.21,<2.0.0)"] + +[[package]] +name = "llama-index-readers-llama-parse" +version = "0.3.0" +description = "llama-index readers llama-parse integration" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "llama_index_readers_llama_parse-0.3.0-py3-none-any.whl", hash = "sha256:1973cc710dbd5e110c7500c9983ecb45787ad1ff92e6b2113f94a57cf48f3038"}, + {file = "llama_index_readers_llama_parse-0.3.0.tar.gz", hash = "sha256:a5feada0895714dcc41d65dd512c1c38cf70d8ae19947cff82b80d58e6aa367e"}, +] + +[package.dependencies] +llama-index-core = ">=0.11.0,<0.12.0" +llama-parse = ">=0.5.0" + +[[package]] +name = "llama-parse" +version = "0.5.15" +description = "Parse files into RAG-Optimized formats." optional = false -python-versions = ">=3.9" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "libcst-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:279b54568ea1f25add50ea4ba3d76d4f5835500c82f24d54daae4c5095b986aa"}, - {file = "libcst-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3401dae41fe24565387a65baee3887e31a44e3e58066b0250bc3f3ccf85b1b5a"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1989fa12d3cd79118ebd29ebe2a6976d23d509b1a4226bc3d66fcb7cb50bd5d"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addc6d585141a7677591868886f6bda0577529401a59d210aa8112114340e129"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d71001cb25e94cfe8c3d997095741a8c4aa7a6d234c0f972bc42818c88dfaf"}, - {file = "libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6227562fc5c9c1efd15dfe90b0971ae254461b8b6b23c1b617139b6003de1c1"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3399e6c95df89921511b44d8c5bf6a75bcbc2d51f1f6429763609ba005c10f6b"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48601e3e590e2d6a7ab8c019cf3937c70511a78d778ab3333764531253acdb33"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42797309bb725f0f000510d5463175ccd7155395f09b5e7723971b0007a976d"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb4e42ea107a37bff7f9fdbee9532d39f9ea77b89caa5c5112b37057b12e0838"}, - {file = "libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7ece51d935bc9bf60b528473d2e5cc67cbb88e2f8146297e40ee2c7d80be6f13"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:81653dea1cdfa4c6520a7c5ffb95fa4d220cbd242e446c7a06d42d8636bfcbba"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6abce0e66bba2babfadc20530fd3688f672d565674336595b4623cd800b91ef"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da9d7dc83801aba3b8d911f82dc1a375db0d508318bad79d9fb245374afe068"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c54aa66c86d8ece9c93156a2cf5ca512b0dce40142fe9e072c86af2bf892411"}, - {file = "libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8ecdba8934632b4dadacb666cd3816627a6ead831b806336972ccc4ba7ca0e9"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e54c777b8d27339b70f304d16fc8bc8674ef1bd34ed05ea874bf4921eb5a313"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:061d6855ef30efe38b8a292b7e5d57c8e820e71fc9ec9846678b60a934b53bbb"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0abf627ee14903d05d0ad9b2c6865f1b21eb4081e2c7bea1033f85db2b8bae"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d024f44059a853b4b852cfc04fec33e346659d851371e46fc8e7c19de24d3da9"}, - {file = "libcst-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c6a8faab9da48c5b371557d0999b4ca51f4f2cbd37ee8c2c4df0ac01c781465"}, - {file = "libcst-1.4.0.tar.gz", hash = "sha256:449e0b16604f054fa7f27c3ffe86ea7ef6c409836fe68fe4e752a1894175db00"}, + {file = "llama_parse-0.5.15-py3-none-any.whl", hash = "sha256:7a3506c7d3ae5a8e68c70a457a7213d2698e26abcef1d7a989eb9771cd73ae60"}, + {file = "llama_parse-0.5.15.tar.gz", hash = "sha256:ecb009f71c8b4c657085ca81808a922c80785810e38b10f3b46f03cfd29ba92a"}, ] [package.dependencies] -pyyaml = ">=5.2" - -[package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.6)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.6.0)", "usort (==1.0.8.post1)"] +click = ">=8.1.7,<9.0.0" +llama-index-core = ">=0.11.0" +pydantic = "!=2.10" [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] name = "marshmallow" -version = "3.22.0" +version = "3.23.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, - {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, + {file = "marshmallow-3.23.1-py3-none-any.whl", hash = "sha256:fece2eb2c941180ea1b7fcbd4a83c51bfdd50093fdd3ad2585ee5e1df2508491"}, + {file = "marshmallow-3.23.1.tar.gz", hash = "sha256:3a8dfda6edd8dcdbf216c0ede1d1e78d230a6dc9c5a088f58c4083b974a0d468"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "simplejson"] [[package]] name = "msgspec" @@ -1284,50 +2133,89 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "networkx" -version = "3.1" +version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, - {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, + {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, + {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, ] [package.extras] -default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] -developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] -doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] -test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] +default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "ninja" -version = "1.11.1.1" +version = "1.11.1.2" description = "Ninja is a small build system with a focus on speed" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "ninja-1.11.1.2-py3-none-macosx_10_9_universal2.whl", hash = "sha256:1cfbb845095ea09da8c089375a8f999e75f4817d01506297c66181b533175647"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ab4068ff7ff1f895485ad604116165b05d6810c802170a7f22c09dd678d5587d"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:33d258809c8eda81f9d80e18a081a6eef3215e5fd1ba8902400d786641994e89"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed25892c16e49e66383a8db6a67a9f33b41230fc485426094d7da51e2255ec2b"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:232767144401847db62e8392047866698bb3678158a1ae4400a97111110e90f2"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9200247cf4c1643a67d079836b8dd31a362e34e618b50b5e3a5c0d0171efc442"}, + {file = "ninja-1.11.1.2-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c9c36f6e6f8946c7271b0ed14d98fc3ea467a0c0954fb73f5f656c42667d943"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:3e815e4147832b17ec38417efcb31df51671ae273f083409304c7cc32a14dd1a"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ecf3df324b56fdfb0872990a71e706efdae286e010310816c72b6bf24431711b"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:cb6b476eb4e84c0efcfd3ab04f660dedce8adb854b56b043639312f3af176df6"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:508fb93395a5c82a4d99d30fce0cbaf5cb2bd33e5c1dc9faaa080e199802dbc9"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:52af7f45750c5c288d566fd0c927ed9bb0d8f2e50803709f582a42bcc4ec167b"}, + {file = "ninja-1.11.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99fc4b87299242e10d7edd1c7737fdfb1269019e32f9f4267630887f6183a49e"}, + {file = "ninja-1.11.1.2-py3-none-win32.whl", hash = "sha256:949e23cb2e79a33ea37d23a07d26846d2e75464e8e6940f8751fe964bc141dfa"}, + {file = "ninja-1.11.1.2-py3-none-win_amd64.whl", hash = "sha256:0bca4179119426a3c3c9d5661c3b244d68781064e50907a1e066bc55edc18e06"}, + {file = "ninja-1.11.1.2-py3-none-win_arm64.whl", hash = "sha256:ee7b1924c28e6cab5b866f7b229f07777d25d8cfccbbedf3da5ffb4f72f57877"}, + {file = "ninja-1.11.1.2.tar.gz", hash = "sha256:4fbd07b2b4232543726abafdd350453a2fabef4527664ca0e491c578aee5f857"}, +] + +[package.extras] +test = ["coverage (>=4.2)", "importlib_metadata (>=2.0)", "pytest (>=6.0)", "pytest-cov (>=3)"] + +[[package]] +name = "nltk" +version = "3.9.1" +description = "Natural Language Toolkit" +optional = false +python-versions = ">=3.8" files = [ - {file = "ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885"}, - {file = "ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a"}, - {file = "ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c"}, + {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, + {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, ] +[package.dependencies] +click = "*" +joblib = "*" +regex = ">=2021.8.3" +tqdm = "*" + [package.extras] -test = ["codecov (>=2.0.5)", "coverage (>=4.2)", "flake8 (>=3.0.4)", "pytest (>=4.5.0)", "pytest-cov (>=2.7.1)", "pytest-runner (>=5.1)", "pytest-virtualenv (>=1.7.0)", "virtualenv (>=15.0.3)"] +all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"] +corenlp = ["requests"] +machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] [[package]] name = "numpy" @@ -1392,13 +2280,13 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.51.0" +version = "1.55.2" description = "The official Python library for the openai API" optional = false -python-versions = ">=3.7.1" +python-versions = ">=3.8" files = [ - {file = "openai-1.51.0-py3-none-any.whl", hash = "sha256:d9affafb7e51e5a27dce78589d4964ce4d6f6d560307265933a94b2e3f3c5d2c"}, - {file = "openai-1.51.0.tar.gz", hash = "sha256:8dc4f9d75ccdd5466fc8c99a952186eddceb9fd6ba694044773f3736a847149d"}, + {file = "openai-1.55.2-py3-none-any.whl", hash = "sha256:3027c7fa4a33ed759f4a3d076093fcfa1c55658660c889bec33f651e2dc77922"}, + {file = "openai-1.55.2.tar.gz", hash = "sha256:5cc0b1162b65dcdf670b4b41448f18dd470d2724ca04821ab1e86b6b4e88650b"}, ] [package.dependencies] @@ -1416,99 +2304,343 @@ datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] [[package]] name = "orjson" -version = "3.10.7" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, - {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, - {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, - {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, - {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, - {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, - {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, - {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, - {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, - {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, - {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, - {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, - {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, - {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, - {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, - {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, - {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, - {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, - {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, - {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, - {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, - {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] +[[package]] +name = "outcome" +version = "1.3.0.post0" +description = "Capture the outcome of Python function calls." +optional = false +python-versions = ">=3.7" +files = [ + {file = "outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b"}, + {file = "outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8"}, +] + +[package.dependencies] +attrs = ">=19.2.0" + [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""} +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pillow" +version = "11.0.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + [[package]] name = "playwright" -version = "1.47.0" +version = "1.49.0" description = "A high-level API to automate web browsers" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341"}, - {file = "playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955"}, - {file = "playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249"}, - {file = "playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974"}, - {file = "playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280"}, + {file = "playwright-1.49.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:704532a2d8ba580ec9e1895bfeafddce2e3d52320d4eb8aa38e80376acc5cbb0"}, + {file = "playwright-1.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e453f02c4e5cc2db7e9759c47e7425f32e50ac76c76b7eb17c69eed72f01c4d8"}, + {file = "playwright-1.49.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:37ae985309184472946a6eb1a237e5d93c9e58a781fa73b75c8751325002a5d4"}, + {file = "playwright-1.49.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:68d94beffb3c9213e3ceaafa66171affd9a5d9162e0c8a3eed1b1132c2e57598"}, + {file = "playwright-1.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f12d2aecdb41fc25a624cb15f3e8391c252ebd81985e3d5c1c261fe93779345"}, + {file = "playwright-1.49.0-py3-none-win32.whl", hash = "sha256:91103de52d470594ad375b512d7143fa95d6039111ae11a93eb4fe2f2b4a4858"}, + {file = "playwright-1.49.0-py3-none-win_amd64.whl", hash = "sha256:34d28a2c2d46403368610be4339898dc9c34eb9f7c578207b4715c49743a072a"}, ] [package.dependencies] -greenlet = "3.0.3" +greenlet = "3.1.1" pyee = "12.0.0" [[package]] @@ -1526,35 +2658,150 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "poethepoet" +version = "0.25.1" +description = "A task runner that works well with poetry." +optional = false +python-versions = ">=3.8" +files = [ + {file = "poethepoet-0.25.1-py3-none-any.whl", hash = "sha256:fee433f68424593bca6b357f0bf997d64edf42c7305c0d5d335bd570b8d2352b"}, + {file = "poethepoet-0.25.1.tar.gz", hash = "sha256:98f4446533a4b2bdb08843e211f918b1f2e7f8baf6d1803ef78f64661ed62463"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +tomli = ">=1.2.2" + +[package.extras] +poetry-plugin = ["poetry (>=1.0,<2.0)"] + [[package]] name = "primp" -version = "0.6.3" +version = "0.8.1" description = "HTTP client that can impersonate web browsers, mimicking their headers and `TLS/JA3/JA4/HTTP2` fingerprints" optional = false python-versions = ">=3.8" files = [ - {file = "primp-0.6.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bdbe6a7cdaaf5c9ed863432a941f4a75bd4c6ff626cbc8d32fc232793c70ba06"}, - {file = "primp-0.6.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:eeb53eb987bdcbcd85740633470255cab887d921df713ffa12a36a13366c9cdb"}, - {file = "primp-0.6.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78da53d3c92a8e3f05bd3286ac76c291f1b6fe5e08ea63b7ba92b0f9141800bb"}, - {file = "primp-0.6.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:86337b44deecdac752bd8112909987fc9fa9b894f30191c80a164dc8f895da53"}, - {file = "primp-0.6.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d3cd9a22b97f3eae42b2a5fb99f00480daf4cd6d9b139e05b0ffb03f7cc037f3"}, - {file = "primp-0.6.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7732bec917e2d3c48a31cdb92e1250f4ad6203a1aa4f802bd9abd84f2286a1e0"}, - {file = "primp-0.6.3-cp38-abi3-win_amd64.whl", hash = "sha256:1e4113c34b86c676ae321af185f03a372caef3ee009f1682c2d62e30ec87348c"}, - {file = "primp-0.6.3.tar.gz", hash = "sha256:17d30ebe26864defad5232dbbe1372e80483940012356e1f68846bb182282039"}, + {file = "primp-0.8.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8294db817701ad76b6a186c16e22cc49d36fac5986647a83657ad4a58ddeee42"}, + {file = "primp-0.8.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:e8117531dcdb0dbcf9855fdbac73febdde5967ca0332a2c05b5961d2fbcfe749"}, + {file = "primp-0.8.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:993cc4284e8c5c858254748f078e872ba250c9339d64398dc000a8f9cffadda3"}, + {file = "primp-0.8.1-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4a27ac642be5c616fc5f139a5ad391dcd0c5964ace56fe6cf31cbffb972a7480"}, + {file = "primp-0.8.1-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:e8483b8d9eec9fc43d77bb448555466030f29cdd99d9375eb75155e9f832e5bd"}, + {file = "primp-0.8.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:92f5f8267216252cfb27f2149811e14682bb64f0c5d37f00d218d1592e02f0b9"}, + {file = "primp-0.8.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:98f7f3a9481c55c56e7eff9024f29e16379a87d5b0a1b683e145dd8fcbdcc46b"}, + {file = "primp-0.8.1-cp38-abi3-win_amd64.whl", hash = "sha256:6f0018a26be787431504e32548b296a278abbe85da43bcbaf2d4982ac3dcd332"}, + {file = "primp-0.8.1.tar.gz", hash = "sha256:ddf05754a7b70d59df8a014a8585e418f9c04e0b69065bab6633f4a9b92bad93"}, ] [package.extras] dev = ["certifi", "pytest (>=8.1.1)"] [[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.8" files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, ] [[package]] @@ -1568,22 +2815,33 @@ files = [ {file = "pycnite-2024.7.31.tar.gz", hash = "sha256:5125f1c95aef4a23b9bec3b32fae76873dcd46324fa68e39c10fa852ecdea340"}, ] +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"}, + {file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"}, ] [package.dependencies] annotated-types = ">=0.6.0" email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.23.4" -typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} +pydantic-core = "2.27.1" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -1591,100 +2849,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -1692,13 +2961,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.5.2" +version = "2.6.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"}, - {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"}, + {file = "pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87"}, + {file = "pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0"}, ] [package.dependencies] @@ -1767,18 +3036,37 @@ requests = [ [[package]] name = "pyparsing" -version = "3.1.4" +version = "3.2.0" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.9" files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, + {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, + {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, ] [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pypdf" +version = "5.1.0" +description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pypdf-5.1.0-py3-none-any.whl", hash = "sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc"}, + {file = "pypdf-5.1.0.tar.gz", hash = "sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740"}, +] + +[package.extras] +crypto = ["cryptography"] +cryptodome = ["PyCryptodome"] +dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "pytest-socket", "pytest-timeout", "pytest-xdist", "wheel"] +docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] +full = ["Pillow (>=8.0.0)", "cryptography"] +image = ["Pillow (>=8.0.0)"] + [[package]] name = "pysocks" version = "1.7.1" @@ -1793,27 +3081,73 @@ files = [ [[package]] name = "pytest" -version = "6.2.5" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, + {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, +] + +[package.dependencies] +coverage = {version = ">=7.5", extras = ["toml"]} +pytest = ">=4.6" [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" [[package]] name = "python-dotenv" @@ -1831,27 +3165,21 @@ cli = ["click (>=5.0)"] [[package]] name = "pytype" -version = "2024.9.13" +version = "2024.10.11" description = "Python type inferencer" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "pytype-2024.9.13-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:52c0005d220b27f9c933e4077de700c4e8171abce0c2af72f4c6263a85ff5bce"}, - {file = "pytype-2024.9.13-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d5dc847c2fe98bac044f956e2fc9f074f09704b64436522b81ede7dd5fa3653"}, - {file = "pytype-2024.9.13-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529f19141c6170d96a38909df430ca52e6904eaef851ad2690cf632f17d2c195"}, - {file = "pytype-2024.9.13-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:38f3eddf05d8530ef16d3d7c2da2556148b9975fc7c3405ac3073022e1a7434b"}, - {file = "pytype-2024.9.13-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b530eae5ab421a2dc9c4ef53f68629c5a622545150ae9702dbb811f56852a63"}, - {file = "pytype-2024.9.13-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb9eaaaf6c33e2716fdce1cf4166d3e5099372d8898b69ab7673225928096456"}, - {file = "pytype-2024.9.13-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:53b767d85f374c7483c8b2849dceb811a15fcb01520e245dd82bd7c0e2befefb"}, - {file = "pytype-2024.9.13-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:176a5bbc0cb0882918a0b48818b95df2c15811e3a8391da089ffc5b33fea7013"}, - {file = "pytype-2024.9.13-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bdaf1eaaf17a13741f67686c2d4c94c30279cd682c7e4cf535e41fc911b0e59"}, - {file = "pytype-2024.9.13-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:425011cc45fba8c83af796155049f9db89d11e8aedbfb21bc1c99408f4a2c4e3"}, - {file = "pytype-2024.9.13-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e500727967b843488c1978114778162ef00fee9be49dfa5b4758dcbbcc55dd9"}, - {file = "pytype-2024.9.13-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b40beab6ef04fc260d86a8ef47b25d1b525dbc4cfbcb73151fd74210c176df"}, - {file = "pytype-2024.9.13-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:b5fdc24b60938ee846dfbdf08b5ea96e934e7d69c34eb1f8fb7707083d177f0e"}, - {file = "pytype-2024.9.13-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8dcfd509118c2d7e0787e72832b45e30037af1c29dfcb733a7e8014f58337287"}, - {file = "pytype-2024.9.13-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9df731062dc18518a46135c4825ad966e1a275ffc0723dd62f9771b420889da0"}, - {file = "pytype-2024.9.13.tar.gz", hash = "sha256:941046ca0f1c43b79162bb51836fef0ba6608012d99f6833148c249f22216f26"}, + {file = "pytype-2024.10.11-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:1c5a43b132b19928a38ba1dbcf8f4e3f67a41ea26087ccf26ae371c4076c3809"}, + {file = "pytype-2024.10.11-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dd9ecb48aa46ecef14b39f1bbe8ff7e586e499639a056c05bd4436ca0b35d82"}, + {file = "pytype-2024.10.11-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37d8dfdf23679abfdfe047efef7239a438a038e580d7e0767c0403a6be07cea0"}, + {file = "pytype-2024.10.11-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:2e31a964aa82e1ac317adbe17b77010e4f362882df1ce7ad15ef0cf0bb97039f"}, + {file = "pytype-2024.10.11-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15e2f39590cc08ef8e6704cfa5c1db6fbbee2799891f9d8adbf821f883a54745"}, + {file = "pytype-2024.10.11-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ead3408fc9622ba8a357c9a6b9b49059a9b8add0a3b8390a9ab490f62a984005"}, + {file = "pytype-2024.10.11-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:cdc881cce9541a475ec48989a5ab889e6274a85afbf6da0e30266d0823f66d42"}, + {file = "pytype-2024.10.11-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13327d0d17b981fe2660dd3a69f97bf09a526f93debc40bb44b240628e0b55c1"}, + {file = "pytype-2024.10.11-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb98711679e631b01b09b09185504fbf38d60f119280918e244a602cf843b0fe"}, + {file = "pytype-2024.10.11.tar.gz", hash = "sha256:ae5ff82f0b07d5ad68d4ec32a3e8de44fad6ed565a821a76aca50a14df382274"}, ] [package.dependencies] @@ -1861,7 +3189,7 @@ importlab = ">=0.8" jinja2 = ">=3.1.2" libcst = ">=1.0.1" msgspec = ">=0.18.6" -networkx = "<3.2" +networkx = ">=2.8" ninja = ">=1.10.0.post2" pycnite = ">=2024.07.31" pydot = ">=1.4.2" @@ -1869,6 +3197,17 @@ tabulate = ">=0.8.10" toml = ">=0.10.2" typing-extensions = ">=4.3.0" +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1931,6 +3270,109 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -2012,25 +3454,54 @@ files = [ {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, ] +[[package]] +name = "selenium" +version = "4.27.1" +description = "Official Python bindings for Selenium WebDriver" +optional = false +python-versions = ">=3.8" +files = [ + {file = "selenium-4.27.1-py3-none-any.whl", hash = "sha256:b89b1f62b5cfe8025868556fe82360d6b649d464f75d2655cb966c8f8447ea18"}, + {file = "selenium-4.27.1.tar.gz", hash = "sha256:5296c425a75ff1b44d0d5199042b36a6d1ef76c04fb775b97b40be739a9caae2"}, +] + +[package.dependencies] +certifi = ">=2021.10.8" +trio = ">=0.17,<1.0" +trio-websocket = ">=0.9,<1.0" +typing_extensions = ">=4.9,<5.0" +urllib3 = {version = ">=1.26,<3", extras = ["socks"]} +websocket-client = ">=1.8,<2.0" + [[package]] name = "setuptools" -version = "75.1.0" +version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, - {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, + {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, + {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] + +[[package]] +name = "sgmllib3k" +version = "1.0.0" +description = "Py3k port of sgmllib." +optional = false +python-versions = "*" +files = [ + {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, +] [[package]] name = "six" @@ -2054,6 +3525,17 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + [[package]] name = "soupsieve" version = "2.6" @@ -2080,64 +3562,72 @@ requests = "*" [[package]] name = "sqlalchemy" -version = "2.0.35" +version = "2.0.36" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-win32.whl", hash = "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-win_amd64.whl", hash = "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-win32.whl", hash = "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-win_amd64.whl", hash = "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f021d334f2ca692523aaf7bbf7592ceff70c8594fad853416a81d66b35e3abf9"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c3f58cf91683102f2f0265c0db3bd3892e9eedabe059720492dbaa4f922da1"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:032d979ce77a6c2432653322ba4cbeabf5a6837f704d16fa38b5a05d8e21fa00"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:2e795c2f7d7249b75bb5f479b432a51b59041580d20599d4e112b5f2046437a3"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:cc32b2990fc34380ec2f6195f33a76b6cdaa9eecf09f0c9404b74fc120aef36f"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-win32.whl", hash = "sha256:9509c4123491d0e63fb5e16199e09f8e262066e58903e84615c301dde8fa2e87"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-win_amd64.whl", hash = "sha256:3655af10ebcc0f1e4e06c5900bb33e080d6a1fa4228f502121f28a3b1753cde5"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-win32.whl", hash = "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-win_amd64.whl", hash = "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f"}, - {file = "SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1"}, - {file = "sqlalchemy-2.0.35.tar.gz", hash = "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} + {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-win32.whl", hash = "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-win_amd64.whl", hash = "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-win32.whl", hash = "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-win_amd64.whl", hash = "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-win32.whl", hash = "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-win_amd64.whl", hash = "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a"}, + {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"}, + {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} typing-extensions = ">=4.6.0" [package.extras] @@ -2146,7 +3636,7 @@ aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] mssql-pyodbc = ["pyodbc"] @@ -2167,13 +3657,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "starlette" -version = "0.38.6" +version = "0.41.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"}, - {file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"}, + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, ] [package.dependencies] @@ -2182,6 +3672,17 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "striprtf" +version = "0.0.26" +description = "A simple library to convert rtf to text" +optional = false +python-versions = "*" +files = [ + {file = "striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb"}, + {file = "striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa"}, +] + [[package]] name = "tabulate" version = "0.9.0" @@ -2198,13 +3699,13 @@ widechars = ["wcwidth"] [[package]] name = "tenacity" -version = "8.5.0" +version = "8.3.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" files = [ - {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, - {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, + {file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"}, + {file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"}, ] [package.extras] @@ -2225,6 +3726,53 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] +[[package]] +name = "tiktoken" +version = "0.8.0" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tiktoken-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e"}, + {file = "tiktoken-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e13f37bc4ef2d012731e93e0fef21dc3b7aea5bb9009618de9a4026844e560"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13d13c981511331eac0d01a59b5df7c0d4060a8be1e378672822213da51e0a2"}, + {file = "tiktoken-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6b2ddbc79a22621ce8b1166afa9f9a888a664a579350dc7c09346a3b5de837d9"}, + {file = "tiktoken-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d8c2d0e5ba6453a290b86cd65fc51fedf247e1ba170191715b049dac1f628005"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d622d8011e6d6f239297efa42a2657043aaed06c4f68833550cac9e9bc723ef1"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2efaf6199717b4485031b4d6edb94075e4d79177a172f38dd934d911b588d54a"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5637e425ce1fc49cf716d88df3092048359a4b3bbb7da762840426e937ada06d"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb0e352d1dbe15aba082883058b3cce9e48d33101bdaac1eccf66424feb5b47"}, + {file = "tiktoken-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56edfefe896c8f10aba372ab5706b9e3558e78db39dd497c940b47bf228bc419"}, + {file = "tiktoken-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:326624128590def898775b722ccc327e90b073714227175ea8febbc920ac0a99"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04"}, + {file = "tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc"}, + {file = "tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953"}, + {file = "tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7"}, + {file = "tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17807445f0cf1f25771c9d86496bd8b5c376f7419912519699f3cc4dc5c12e"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:886f80bd339578bbdba6ed6d0567a0d5c6cfe198d9e587ba6c447654c65b8edc"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6adc8323016d7758d6de7313527f755b0fc6c72985b7d9291be5d96d73ecd1e1"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b591fb2b30d6a72121a80be24ec7a0e9eb51c5500ddc7e4c2496516dd5e3816b"}, + {file = "tiktoken-0.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:845287b9798e476b4d762c3ebda5102be87ca26e5d2c9854002825d60cdb815d"}, + {file = "tiktoken-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:1473cfe584252dc3fa62adceb5b1c763c1874e04511b197da4e6de51d6ce5a02"}, + {file = "tiktoken-0.8.0.tar.gz", hash = "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + [[package]] name = "toml" version = "0.10.2" @@ -2236,26 +3784,72 @@ files = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +[[package]] +name = "tomli" +version = "2.1.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, +] + [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "trio" +version = "0.27.0" +description = "A friendly Python library for async concurrency and I/O" +optional = false +python-versions = ">=3.8" +files = [ + {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"}, + {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"}, +] + +[package.dependencies] +attrs = ">=23.2.0" +cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""} +idna = "*" +outcome = "*" +sniffio = ">=1.3.0" +sortedcontainers = "*" + +[[package]] +name = "trio-websocket" +version = "0.11.1" +description = "WebSocket library for Trio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"}, + {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"}, +] + +[package.dependencies] +trio = ">=0.11" +wsproto = ">=0.14" + [[package]] name = "tweepy" version = "4.14.0" @@ -2305,6 +3899,17 @@ files = [ mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + [[package]] name = "urllib3" version = "2.2.3" @@ -2316,6 +3921,9 @@ files = [ {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] +[package.dependencies] +pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} + [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] @@ -2340,6 +3948,73 @@ h11 = ">=0.8" [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +[[package]] +name = "uvloop" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + [[package]] name = "wikipedia" version = "1.4.0" @@ -2354,112 +4029,191 @@ files = [ beautifulsoup4 = "*" requests = ">=2.0.0,<3.0.0" +[[package]] +name = "wrapt" +version = "1.17.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +files = [ + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, +] + +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + [[package]] name = "yarl" -version = "1.13.1" +version = "1.18.0" description = "Yet another URL library" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:82e692fb325013a18a5b73a4fed5a1edaa7c58144dc67ad9ef3d604eccd451ad"}, - {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df4e82e68f43a07735ae70a2d84c0353e58e20add20ec0af611f32cd5ba43fb4"}, - {file = "yarl-1.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec9dd328016d8d25702a24ee274932aebf6be9787ed1c28d021945d264235b3c"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5820bd4178e6a639b3ef1db8b18500a82ceab6d8b89309e121a6859f56585b05"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86c438ce920e089c8c2388c7dcc8ab30dfe13c09b8af3d306bcabb46a053d6f7"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3de86547c820e4f4da4606d1c8ab5765dd633189791f15247706a2eeabc783ae"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca53632007c69ddcdefe1e8cbc3920dd88825e618153795b57e6ebcc92e752a"}, - {file = "yarl-1.13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4ee1d240b84e2f213565f0ec08caef27a0e657d4c42859809155cf3a29d1735"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c49f3e379177f4477f929097f7ed4b0622a586b0aa40c07ac8c0f8e40659a1ac"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5c5e32fef09ce101fe14acd0f498232b5710effe13abac14cd95de9c274e689e"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab9524e45ee809a083338a749af3b53cc7efec458c3ad084361c1dbf7aaf82a2"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b1481c048fe787f65e34cb06f7d6824376d5d99f1231eae4778bbe5c3831076d"}, - {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31497aefd68036d8e31bfbacef915826ca2e741dbb97a8d6c7eac66deda3b606"}, - {file = "yarl-1.13.1-cp310-cp310-win32.whl", hash = "sha256:1fa56f34b2236f5192cb5fceba7bbb09620e5337e0b6dfe2ea0ddbd19dd5b154"}, - {file = "yarl-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:1bbb418f46c7f7355084833051701b2301092e4611d9e392360c3ba2e3e69f88"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:216a6785f296169ed52cd7dcdc2612f82c20f8c9634bf7446327f50398732a51"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40c6e73c03a6befb85b72da213638b8aaa80fe4136ec8691560cf98b11b8ae6e"}, - {file = "yarl-1.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2430cf996113abe5aee387d39ee19529327205cda975d2b82c0e7e96e5fdabdc"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fb4134cc6e005b99fa29dbc86f1ea0a298440ab6b07c6b3ee09232a3b48f495"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309c104ecf67626c033845b860d31594a41343766a46fa58c3309c538a1e22b2"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f90575e9fe3aae2c1e686393a9689c724cd00045275407f71771ae5d690ccf38"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2e1626be8712333a9f71270366f4a132f476ffbe83b689dd6dc0d114796c74"}, - {file = "yarl-1.13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b66c87da3c6da8f8e8b648878903ca54589038a0b1e08dde2c86d9cd92d4ac9"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cf1ad338620249f8dd6d4b6a91a69d1f265387df3697ad5dc996305cf6c26fb2"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9915300fe5a0aa663c01363db37e4ae8e7c15996ebe2c6cce995e7033ff6457f"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:703b0f584fcf157ef87816a3c0ff868e8c9f3c370009a8b23b56255885528f10"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1d8e3ca29f643dd121f264a7c89f329f0fcb2e4461833f02de6e39fef80f89da"}, - {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7055bbade838d68af73aea13f8c86588e4bcc00c2235b4b6d6edb0dbd174e246"}, - {file = "yarl-1.13.1-cp311-cp311-win32.whl", hash = "sha256:a3442c31c11088e462d44a644a454d48110f0588de830921fd201060ff19612a"}, - {file = "yarl-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:81bad32c8f8b5897c909bf3468bf601f1b855d12f53b6af0271963ee67fff0d2"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f452cc1436151387d3d50533523291d5f77c6bc7913c116eb985304abdbd9ec9"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9cec42a20eae8bebf81e9ce23fb0d0c729fc54cf00643eb251ce7c0215ad49fe"}, - {file = "yarl-1.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d959fe96e5c2712c1876d69af0507d98f0b0e8d81bee14cfb3f6737470205419"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8c837ab90c455f3ea8e68bee143472ee87828bff19ba19776e16ff961425b57"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94a993f976cdcb2dc1b855d8b89b792893220db8862d1a619efa7451817c836b"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b2442a415a5f4c55ced0fade7b72123210d579f7d950e0b5527fc598866e62c"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fdbf0418489525231723cdb6c79e7738b3cbacbaed2b750cb033e4ea208f220"}, - {file = "yarl-1.13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b7f6e699304717fdc265a7e1922561b02a93ceffdaefdc877acaf9b9f3080b8"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bcd5bf4132e6a8d3eb54b8d56885f3d3a38ecd7ecae8426ecf7d9673b270de43"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a93a4557f7fc74a38ca5a404abb443a242217b91cd0c4840b1ebedaad8919d4"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:22b739f99c7e4787922903f27a892744189482125cc7b95b747f04dd5c83aa9f"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2db874dd1d22d4c2c657807562411ffdfabec38ce4c5ce48b4c654be552759dc"}, - {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4feaaa4742517eaceafcbe74595ed335a494c84634d33961214b278126ec1485"}, - {file = "yarl-1.13.1-cp312-cp312-win32.whl", hash = "sha256:bbf9c2a589be7414ac4a534d54e4517d03f1cbb142c0041191b729c2fa23f320"}, - {file = "yarl-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:d07b52c8c450f9366c34aa205754355e933922c79135125541daae6cbf31c799"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:95c6737f28069153c399d875317f226bbdea939fd48a6349a3b03da6829fb550"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cd66152561632ed4b2a9192e7f8e5a1d41e28f58120b4761622e0355f0fe034c"}, - {file = "yarl-1.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6a2acde25be0cf9be23a8f6cbd31734536a264723fca860af3ae5e89d771cd71"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18595e6a2ee0826bf7dfdee823b6ab55c9b70e8f80f8b77c37e694288f5de1"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a31d21089894942f7d9a8df166b495101b7258ff11ae0abec58e32daf8088813"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45f209fb4bbfe8630e3d2e2052535ca5b53d4ce2d2026bed4d0637b0416830da"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f722f30366474a99745533cc4015b1781ee54b08de73260b2bbe13316079851"}, - {file = "yarl-1.13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3bf60444269345d712838bb11cc4eadaf51ff1a364ae39ce87a5ca8ad3bb2c8"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:942c80a832a79c3707cca46bd12ab8aa58fddb34b1626d42b05aa8f0bcefc206"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:44b07e1690f010c3c01d353b5790ec73b2f59b4eae5b0000593199766b3f7a5c"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:396e59b8de7e4d59ff5507fb4322d2329865b909f29a7ed7ca37e63ade7f835c"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3bb83a0f12701c0b91112a11148b5217617982e1e466069d0555be9b372f2734"}, - {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c92b89bffc660f1274779cb6fbb290ec1f90d6dfe14492523a0667f10170de26"}, - {file = "yarl-1.13.1-cp313-cp313-win32.whl", hash = "sha256:269c201bbc01d2cbba5b86997a1e0f73ba5e2f471cfa6e226bcaa7fd664b598d"}, - {file = "yarl-1.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:1d0828e17fa701b557c6eaed5edbd9098eb62d8838344486248489ff233998b8"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8be8cdfe20787e6a5fcbd010f8066227e2bb9058331a4eccddec6c0db2bb85b2"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08d7148ff11cb8e886d86dadbfd2e466a76d5dd38c7ea8ebd9b0e07946e76e4b"}, - {file = "yarl-1.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4afdf84610ca44dcffe8b6c22c68f309aff96be55f5ea2fa31c0c225d6b83e23"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0d12fe78dcf60efa205e9a63f395b5d343e801cf31e5e1dda0d2c1fb618073d"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298c1eecfd3257aa16c0cb0bdffb54411e3e831351cd69e6b0739be16b1bdaa8"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c14c16831b565707149c742d87a6203eb5597f4329278446d5c0ae7a1a43928e"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9bacedbb99685a75ad033fd4de37129449e69808e50e08034034c0bf063f99"}, - {file = "yarl-1.13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:658e8449b84b92a4373f99305de042b6bd0d19bf2080c093881e0516557474a5"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:373f16f38721c680316a6a00ae21cc178e3a8ef43c0227f88356a24c5193abd6"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:45d23c4668d4925688e2ea251b53f36a498e9ea860913ce43b52d9605d3d8177"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f7917697bcaa3bc3e83db91aa3a0e448bf5cde43c84b7fc1ae2427d2417c0224"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5989a38ba1281e43e4663931a53fbf356f78a0325251fd6af09dd03b1d676a09"}, - {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11b3ca8b42a024513adce810385fcabdd682772411d95bbbda3b9ed1a4257644"}, - {file = "yarl-1.13.1-cp38-cp38-win32.whl", hash = "sha256:dcaef817e13eafa547cdfdc5284fe77970b891f731266545aae08d6cce52161e"}, - {file = "yarl-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:7addd26594e588503bdef03908fc207206adac5bd90b6d4bc3e3cf33a829f57d"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0ae6637b173d0c40b9c1462e12a7a2000a71a3258fa88756a34c7d38926911c"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:576365c9f7469e1f6124d67b001639b77113cfd05e85ce0310f5f318fd02fe85"}, - {file = "yarl-1.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78f271722423b2d4851cf1f4fa1a1c4833a128d020062721ba35e1a87154a049"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d74f3c335cfe9c21ea78988e67f18eb9822f5d31f88b41aec3a1ec5ecd32da5"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1891d69a6ba16e89473909665cd355d783a8a31bc84720902c5911dbb6373465"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb382fd7b4377363cc9f13ba7c819c3c78ed97c36a82f16f3f92f108c787cbbf"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8854b9f80693d20cec797d8e48a848c2fb273eb6f2587b57763ccba3f3bd4b"}, - {file = "yarl-1.13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbf2c3f04ff50f16404ce70f822cdc59760e5e2d7965905f0e700270feb2bbfc"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fb9f59f3848edf186a76446eb8bcf4c900fe147cb756fbbd730ef43b2e67c6a7"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ef9b85fa1bc91c4db24407e7c4da93a5822a73dd4513d67b454ca7064e8dc6a3"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:098b870c18f1341786f290b4d699504e18f1cd050ed179af8123fd8232513424"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8c723c91c94a3bc8033dd2696a0f53e5d5f8496186013167bddc3fb5d9df46a3"}, - {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44a4c40a6f84e4d5955b63462a0e2a988f8982fba245cf885ce3be7618f6aa7d"}, - {file = "yarl-1.13.1-cp39-cp39-win32.whl", hash = "sha256:84bbcdcf393139f0abc9f642bf03f00cac31010f3034faa03224a9ef0bb74323"}, - {file = "yarl-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:fc2931ac9ce9c61c9968989ec831d3a5e6fcaaff9474e7cfa8de80b7aff5a093"}, - {file = "yarl-1.13.1-py3-none-any.whl", hash = "sha256:6a5185ad722ab4dd52d5fb1f30dcc73282eb1ed494906a92d1a228d3f89607b0"}, - {file = "yarl-1.13.1.tar.gz", hash = "sha256:ec8cfe2295f3e5e44c51f57272afbd69414ae629ec7c6b27f5a410efc78b70a0"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9"}, + {file = "yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0"}, + {file = "yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc"}, + {file = "yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716"}, + {file = "yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7"}, + {file = "yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75"}, + {file = "yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af"}, + {file = "yarl-1.18.0-cp313-cp313-win32.whl", hash = "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88"}, + {file = "yarl-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89"}, + {file = "yarl-1.18.0-cp39-cp39-win32.whl", hash = "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f"}, + {file = "yarl-1.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5"}, + {file = "yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0"}, + {file = "yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = ">=3.12,<3.13" -content-hash = "4f933143b7f17beaa1c4e8bdc117978de07df01f9b8b0eb4eb11f88182230ee5" +content-hash = "65fb9f49fff44cbd51be6f3360a4eb95c03106a26b2615c0f6ccdd169a561f95" diff --git a/integrations-service/pyproject.toml b/integrations-service/pyproject.toml index 33173f2c2..ee0cfdb95 100644 --- a/integrations-service/pyproject.toml +++ b/integrations-service/pyproject.toml @@ -21,6 +21,18 @@ spider-client = "^0.0.70" browserbase = "^0.3.0" setuptools = "^75.1.0" beartype = "^0.19.0" +tenacity = ">=8.2.0,<8.4.0" +gunicorn = "^23.0.0" +uvloop = "^0.21.0" +selenium = "^4.25.0" +playwright = "^1.48.0" +httpx = "^0.27.2" +pillow = "^11.0.0" +llama-index = "^0.11.22" +llama-parse = "^0.5.13" +cloudinary = "^1.41.0" +environs = "^11.2.1" +arxiv = "^2.1.3" [tool.poe.tasks] format = "ruff format" @@ -31,11 +43,55 @@ check = [ "format", "typecheck", ] +test = "pytest" +codegen = """ +datamodel-codegen \ + --input ../openapi.yaml \ + --input-file-type openapi \ + --output integrations/autogen/ \ + --output-model-type pydantic_v2.BaseModel \ + --strict-types bool \ + --strict-nullable \ + --allow-population-by-field-name \ + --field-include-all-keys \ + --reuse-model \ + --snake-case-field \ + --enum-field-as-literal all \ + --field-constraints \ + --use-operation-id-as-name \ + --use-schema-description \ + --use-field-description \ + --use-annotated \ + --use-default \ + --use-unique-items-as-set \ + --use-subclass-enum \ + --use-union-operator \ + --use-one-literal-as-default \ + --use-double-quotes \ + --use-exact-imports \ + --use-standard-collections \ + --use-non-positive-negative-number-constrained-types \ + --target-python-version 3.12 \ + --treat-dot-as-module \ + --use-title-as-name \ + --collapse-root-models \ + --output-datetime-class AwareDatetime \ + --openapi-scopes schemas \ + --keep-model-order \ + --disable-timestamp""" -[tool.poetry.dev-dependencies] -pytest = "^6.2.5" + +[tool.poetry.group.dev.dependencies] +poethepoet = "^0.25.1" +datamodel-code-generator = "^0.26.2" +pytest = "^8.3.3" pytype = "^2024.9.13" ruff = "^0.6.8" +pytest-cov = "^6.0.0" +pytest-asyncio = "^0.24.0" + +[tool.pytest.ini_options] +asyncio_mode = "auto" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/integrations-service/pytest.ini b/integrations-service/pytest.ini new file mode 100644 index 000000000..351b8de9b --- /dev/null +++ b/integrations-service/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +pythonpath = . +testpaths = tests +python_files = test_*.py +python_classes = Test* +python_functions = test_* \ No newline at end of file diff --git a/integrations-service/tests/__init__.py b/integrations-service/tests/__init__.py new file mode 100644 index 000000000..8685672f8 --- /dev/null +++ b/integrations-service/tests/__init__.py @@ -0,0 +1 @@ +# Empty file to make the tests directory a Python package diff --git a/integrations-service/tests/conftest.py b/integrations-service/tests/conftest.py new file mode 100644 index 000000000..e2db31eb6 --- /dev/null +++ b/integrations-service/tests/conftest.py @@ -0,0 +1,67 @@ +import pytest +from unittest.mock import patch + +from integrations.providers import available_providers +from .mocks.brave import MockBraveSearchClient +from .mocks.email import MockEmailClient +from .mocks.spider import MockSpiderClient +from .mocks.weather import MockWeatherClient +from .mocks.wikipedia import MockWikipediaClient +from .mocks.llama_parse import MockLlamaParseClient + + +@pytest.fixture(autouse=True) +def mock_external_services(): + """Automatically mock all external service clients""" + with patch("langchain_community.tools.BraveSearch", MockBraveSearchClient), patch( + "smtplib.SMTP", MockEmailClient + ), patch( + "langchain_community.document_loaders.SpiderLoader", MockSpiderClient + ), patch( + "langchain_community.utilities.OpenWeatherMapAPIWrapper", MockWeatherClient + ), patch( + "langchain_community.document_loaders.WikipediaLoader", MockWikipediaClient + ), patch("llama_parse.LlamaParse", MockLlamaParseClient): + yield + + +@pytest.fixture +def providers(): + """Fixture that provides access to all available integration providers""" + return available_providers + + +@pytest.fixture +def wikipedia_provider(): + """Fixture that provides access to the Wikipedia provider""" + return available_providers["wikipedia"] + + +@pytest.fixture +def weather_provider(): + """Fixture that provides access to the Weather provider""" + return available_providers["weather"] + + +@pytest.fixture +def spider_provider(): + """Fixture that provides access to the Spider provider""" + return available_providers["spider"] + + +@pytest.fixture +def brave_provider(): + """Fixture that provides access to the Brave provider""" + return available_providers["brave"] + + +@pytest.fixture +def email_provider(): + """Fixture that provides access to the Email provider""" + return available_providers["email"] + + +@pytest.fixture +def llama_parse_provider(): + """Fixture that provides access to the LlamaParse provider""" + return available_providers["llama_parse"] diff --git a/integrations-service/tests/mocks/__init__.py b/integrations-service/tests/mocks/__init__.py new file mode 100644 index 000000000..487dd078a --- /dev/null +++ b/integrations-service/tests/mocks/__init__.py @@ -0,0 +1 @@ +"""Mock implementations of external integration libraries""" diff --git a/integrations-service/tests/mocks/brave.py b/integrations-service/tests/mocks/brave.py new file mode 100644 index 000000000..958925aed --- /dev/null +++ b/integrations-service/tests/mocks/brave.py @@ -0,0 +1,16 @@ +"""Mock implementation of Brave Search API client""" + + +class MockBraveSearchClient: + def __init__(self, api_key: str): + self.api_key = api_key + + def search(self, query: str) -> str: + """Mock search that returns a fixed response""" + return f"Mock Brave search results for: {query}" + + +class MockBraveSearchException(Exception): + """Mock exception for Brave Search errors""" + + pass diff --git a/integrations-service/tests/mocks/email.py b/integrations-service/tests/mocks/email.py new file mode 100644 index 000000000..5f747ddc3 --- /dev/null +++ b/integrations-service/tests/mocks/email.py @@ -0,0 +1,19 @@ +"""Mock implementation of email client""" + + +class MockEmailClient: + def __init__(self, host: str, port: int, user: str, password: str): + self.host = host + self.port = port + self.user = user + self.password = password + + def send(self, to: str, from_: str, subject: str, body: str) -> bool: + """Mock email sending that always succeeds""" + return True + + +class MockEmailException(Exception): + """Mock exception for email errors""" + + pass diff --git a/integrations-service/tests/mocks/llama_parse.py b/integrations-service/tests/mocks/llama_parse.py new file mode 100644 index 000000000..4ca9bd28a --- /dev/null +++ b/integrations-service/tests/mocks/llama_parse.py @@ -0,0 +1,25 @@ +"""Mock implementation of llama parse client""" + +from typing import List, Dict +from llama_index.core.schema import Document + + +class MockLlamaParseClient: + def __init__(self, api_key: str, result_type: str, num_workers: int, language: str): + self.api_key = api_key + self.result_type = result_type + self.num_workers = num_workers + self.language = language + + async def aload_data(self, file_content: bytes, extra_info: dict) -> List[Dict]: + """Mock loading data that returns fixed documents""" + return [ + Document(page_content="Mock document content 1", metadata=extra_info), + Document(page_content="Mock document content 2", metadata=extra_info), + ] + + +class MockLlamaParseException(Exception): + """Mock exception for llama parse errors""" + + pass diff --git a/integrations-service/tests/mocks/spider.py b/integrations-service/tests/mocks/spider.py new file mode 100644 index 000000000..dc6f01c41 --- /dev/null +++ b/integrations-service/tests/mocks/spider.py @@ -0,0 +1,27 @@ +"""Mock implementation of web spider client""" + +from typing import List +from langchain_core.documents import Document +from pydantic import AnyUrl + + +class MockSpiderClient: + def __init__(self, api_key: str): + self.api_key = api_key + + def crawl(self, url: AnyUrl, mode: str = "scrape") -> List[Document]: + """Mock crawl that returns fixed documents""" + return [ + Document( + page_content="Mock crawled content 1", metadata={"source": str(url)} + ), + Document( + page_content="Mock crawled content 2", metadata={"source": str(url)} + ), + ] + + +class MockSpiderException(Exception): + """Mock exception for spider errors""" + + pass diff --git a/integrations-service/tests/mocks/weather.py b/integrations-service/tests/mocks/weather.py new file mode 100644 index 000000000..4fa4c357d --- /dev/null +++ b/integrations-service/tests/mocks/weather.py @@ -0,0 +1,16 @@ +"""Mock implementation of weather API client""" + + +class MockWeatherClient: + def __init__(self, api_key: str): + self.api_key = api_key + + def get_weather(self, location: str) -> str: + """Mock weather lookup that returns fixed data""" + return f"Mock weather data for {location}: Sunny, 72°F" + + +class MockWeatherException(Exception): + """Mock exception for weather API errors""" + + pass diff --git a/integrations-service/tests/mocks/wikipedia.py b/integrations-service/tests/mocks/wikipedia.py new file mode 100644 index 000000000..19b11d140 --- /dev/null +++ b/integrations-service/tests/mocks/wikipedia.py @@ -0,0 +1,25 @@ +"""Mock implementation of Wikipedia API client""" + +from typing import List +from langchain_core.documents import Document + + +class MockWikipediaClient: + def __init__(self, query: str, load_max_docs: int = 2): + """Mock Wikipedia search that returns fixed documents""" + self.result = [ + Document( + page_content=f"Mock Wikipedia content about {query}", + metadata={"source": f"wikipedia/{query}"}, + ) + for _ in range(load_max_docs) + ] + + def load(self, *args, **kwargs) -> List[Document]: + return self.result + + +class MockWikipediaException(Exception): + """Mock exception for Wikipedia API errors""" + + pass diff --git a/integrations-service/tests/test_provider_execution.py b/integrations-service/tests/test_provider_execution.py new file mode 100644 index 000000000..9b96ee51b --- /dev/null +++ b/integrations-service/tests/test_provider_execution.py @@ -0,0 +1,49 @@ +"""Tests for provider execution using mocks""" + +import pytest + +from integrations.autogen.Tools import ( + WikipediaSearchArguments, +) +from integrations.utils.execute_integration import execute_integration + + +@pytest.mark.asyncio +async def test_weather_get_mock(wikipedia_provider): + """Test wikipedia lookup with mock client""" + query = "London" + + result = await execute_integration( + provider="wikipedia", + method="search", + arguments=WikipediaSearchArguments(query=query), + ) + + assert len(result.documents) > 0 + assert any([(query in doc.page_content) for doc in result.documents]) + + +# @pytest.mark.asyncio +# async def test_weather_get_direct(): +# """Test weather lookup with mock client""" +# raise NotImplementedError + + +# def test_weather_get_mock(weather_provider): +# """Test weather lookup with mock client""" +# raise NotImplementedError + + +# def test_spider_crawl(spider_provider): +# """Test web crawling with mock client""" +# raise NotImplementedError + + +# def test_brave_search(brave_provider): +# """Test Brave search with mock client""" +# raise NotImplementedError + + +# def test_email_send(email_provider): +# """Test email sending with mock client""" +# raise NotImplementedError diff --git a/integrations-service/tests/test_providers.py b/integrations-service/tests/test_providers.py new file mode 100644 index 000000000..181248944 --- /dev/null +++ b/integrations-service/tests/test_providers.py @@ -0,0 +1,43 @@ +from integrations.models import BaseProvider, BaseProviderMethod, ProviderInfo + + +def test_available_providers(providers): + """Test that the available providers dictionary is properly structured""" + assert isinstance(providers, dict) + assert all(isinstance(key, str) for key in providers.keys()) + assert all(isinstance(value, BaseProvider) for value in providers.values()) + + +def test_provider_structure(providers): + """Test that each provider has the required attributes""" + for provider_name, provider in providers.items(): + assert isinstance(provider.provider, str) + assert isinstance(provider.methods, list) + assert all( + isinstance(method, BaseProviderMethod) for method in provider.methods + ) + assert isinstance(provider.info, ProviderInfo) + + +def test_wikipedia_provider(wikipedia_provider): + """Test Wikipedia provider specific configuration""" + assert wikipedia_provider.provider == "wikipedia" + assert wikipedia_provider.setup is None + assert len(wikipedia_provider.methods) == 1 + assert wikipedia_provider.methods[0].method == "search" + + +def test_weather_provider(weather_provider): + """Test Weather provider specific configuration""" + assert weather_provider.provider == "weather" + assert weather_provider.setup is not None + assert len(weather_provider.methods) == 1 + assert weather_provider.methods[0].method == "get" + + +def test_llama_parse_provider(llama_parse_provider): + """Test LlamaParse provider specific configuration""" + assert llama_parse_provider.provider == "llama_parse" + assert llama_parse_provider.setup is not None + assert len(llama_parse_provider.methods) == 1 + assert llama_parse_provider.methods[0].method == "parse" diff --git a/llm-proxy/docker-compose.yml b/llm-proxy/docker-compose.yml index 8e9403df1..9c422b5dc 100644 --- a/llm-proxy/docker-compose.yml +++ b/llm-proxy/docker-compose.yml @@ -1,9 +1,11 @@ name: julep-llm-proxy x--litellm-base: &litellm-base - image: ghcr.io/berriai/litellm-database:main-v1.46.6 + image: julepai/litellm-database:fix_anthropic_tool_image_content-amd64 restart: unless-stopped hostname: litellm + ports: + - 4000:4000 volumes: - ./litellm-config.yaml:/app/config.yaml - .keys:/app/.keys:ro @@ -11,9 +13,10 @@ x--litellm-base: &litellm-base - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY} - DATABASE_URL=${LITELLM_DATABASE_URL:-postgresql://${LITELLM_POSTGRES_USER:-llmproxy}:${LITELLM_POSTGRES_PASSWORD}@${LITELLM_POSTGRES_HOST:-litellm-db}:${LITELLM_POSTGRES_PORT:-5432}/${LITELLM_POSTGRES_DB:-litellm}?sslmode=${LITELLM_POSTGRES_SSLMODE:-prefer_ssl}} - REDIS_URL=${LITELLM_REDIS_URL:-${LITELLM_REDIS_PROTOCOL:-redis}://${LITELLM_REDIS_USER:-default}:${LITELLM_REDIS_PASSWORD:-${LITELLM_REDIS_PASSWORD}}@${LITELLM_REDIS_HOST:-litellm-redis}:${LITELLM_REDIS_PORT:-6379}} - + - GEMINI_API_KEY=${GEMINI_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} - GROQ_API_KEY=${GROQ_API_KEY} - CLOUDFLARE_API_KEY=${CLOUDFLARE_API_KEY} - CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID} @@ -50,6 +53,7 @@ services: <<: *litellm-base profiles: - self-hosted-db + platform: linux/amd64 depends_on: - litellm-db @@ -68,11 +72,11 @@ services: - POSTGRES_PASSWORD=${LITELLM_POSTGRES_PASSWORD} healthcheck: test: [ "CMD-SHELL", "pg_isready -d ${LITELLM_POSTGRES_DB:-litellm} -U ${LITELLM_POSTGRES_USER:-llmproxy}" ] - + interval: 1s timeout: 5s retries: 10 - + litellm-redis: image: redis/redis-stack-server restart: unless-stopped diff --git a/llm-proxy/litellm-config.yaml b/llm-proxy/litellm-config.yaml index 733cd0ec4..b701c17e8 100644 --- a/llm-proxy/litellm-config.yaml +++ b/llm-proxy/litellm-config.yaml @@ -6,17 +6,18 @@ model_list: # ------------------- # Gemini models + - model_name: gemini-1.5-pro litellm_params: - model: vertex_ai_beta/gemini-1.5-pro + model: gemini/gemini-1.5-pro + api_key: os.environ/GEMINI_API_KEY tags: ["paid"] - vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS -- model_name: claude-3.5-sonnet +- model_name: gemini-1.5-pro-latest litellm_params: - model: vertex_ai/claude-3-5-sonnet@20240620 + model: gemini/gemini-1.5-pro-latest + api_key: os.environ/GEMINI_API_KEY tags: ["paid"] - vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS # OpenAI models - model_name: "gpt-4-turbo" @@ -31,13 +32,37 @@ model_list: tags: ["paid"] api_key: os.environ/OPENAI_API_KEY +- model_name: o1-mini + litellm_params: + model: openai/o1-mini + api_key: os.environ/OPENAI_API_KEY + tags: ["paid"] + +- model_name: o1-preview + litellm_params: + model: openai/o1-preview + api_key: os.environ/OPENAI_API_KEY + tags: ["paid"] + # Anthropic models -- model_name: "claude-3.5-sonnet" +- model_name: "claude-3.5-sonnet-20240620" litellm_params: model: "claude-3-5-sonnet-20240620" tags: ["paid"] api_key: os.environ/ANTHROPIC_API_KEY +- model_name: "claude-3.5-sonnet" + litellm_params: + model: "claude-3-5-sonnet-20241022" + tags: ["paid"] + api_key: os.environ/ANTHROPIC_API_KEY + +- model_name: "claude-3.5-sonnet-20241022" + litellm_params: + model: "claude-3-5-sonnet-20241022" + tags: ["paid"] + api_key: os.environ/ANTHROPIC_API_KEY + - model_name: "claude-3-opus" litellm_params: model: "claude-3-opus-20240229" @@ -69,6 +94,18 @@ model_list: tags: ["paid"] api_key: os.environ/GROQ_API_KEY +# OpenRouter models +- model_name: "mistral-large-2411" + litellm_params: + model: "openrouter/mistralai/mistral-large-2411" + api_key: os.environ/OPENROUTER_API_KEY + tags: ["paid"] + +- model_name: "qwen-2.5-72b-instruct" + litellm_params: + model: "openrouter/qwen/qwen-2.5-72b-instruct" + api_key: os.environ/OPENROUTER_API_KEY + tags: ["paid"] # -*= Embedding models =*- # ------------------------ @@ -148,4 +185,4 @@ router_settings: general_settings: master_key: os.environ/LITELLM_MASTER_KEY database_url: os.environ/LITELLM_DATABASE_URL - enforce_user_param: true \ No newline at end of file + enforce_user_param: true diff --git a/memory-store/Dockerfile b/memory-store/Dockerfile index 852a5f687..d9eb26819 100644 --- a/memory-store/Dockerfile +++ b/memory-store/Dockerfile @@ -12,7 +12,7 @@ FROM rust:1.80.1-bookworm AS builder RUN apt-get update && apt-get install -y liburing-dev # Clone the CozoDB repository with submodules (checkout the specified commit) -ARG COZO_COMMIT=57b7b440fd93440d985f2259eeaaf2ba5cc7569e +ARG COZO_COMMIT=695d0282fa9836bd93b4ff4313ec1cfb514c4f3b RUN \ git clone --depth 1 --recurse-submodules --shallow-submodules https://github.com/cozodb/cozo.git /usr/src/cozo && \ cd /usr/src/cozo && \ @@ -56,6 +56,9 @@ RUN \ mkdir -p $COZO_MNT_DIR $COZO_BACKUP_DIR && \ chmod +x $APP_HOME/bin/cozo && \ chmod +x $APP_HOME/run.sh + +# Copy the options file into the image +COPY ./options ./options # Use tini to manage zombie processes and signal forwarding # https://github.com/krallin/tini diff --git a/memory-store/docker-compose.yml b/memory-store/docker-compose.yml index 2adf50e81..190d1b125 100644 --- a/memory-store/docker-compose.yml +++ b/memory-store/docker-compose.yml @@ -2,7 +2,7 @@ name: julep-memory-store services: memory-store: - image: julepai/memory-store:${TAG} + image: julepai/memory-store:${TAG:-dev} environment: - COZO_AUTH_TOKEN=${COZO_AUTH_TOKEN} - COZO_PORT=${COZO_PORT:-9070} @@ -16,6 +16,14 @@ services: ports: - "9070:9070" + develop: + watch: + - action: sync+restart + path: ./options + target: /data/cozo.db/options + - action: rebuild + path: Dockerfile + labels: ofelia.enabled: "true" ofelia.job-exec.backupcron.schedule: "@every 3h" @@ -33,5 +41,6 @@ services: volumes: cozo_data: + external: true cozo_backup: - external: true \ No newline at end of file + external: true diff --git a/memory-store/options b/memory-store/options new file mode 100644 index 000000000..b11fcc62b --- /dev/null +++ b/memory-store/options @@ -0,0 +1,204 @@ +# This is a RocksDB option file. +# +# For detailed file format spec, please refer to the example file +# in examples/rocksdb_option_file_example.ini +# + +[Version] + rocksdb_version=8.8.1 + options_file_version=1.1 + +[DBOptions] + compaction_readahead_size=2097152 + strict_bytes_per_sync=false + bytes_per_sync=1048576 + max_background_jobs=32 + avoid_flush_during_shutdown=false + max_background_flushes=-1 + delayed_write_rate=16777216 + max_open_files=-1 + max_subcompactions=8 + writable_file_max_buffer_size=5048576 + wal_bytes_per_sync=0 + max_background_compactions=-1 + max_total_wal_size=0 + delete_obsolete_files_period_micros=21600000000 + stats_dump_period_sec=600 + stats_history_buffer_size=1048576 + stats_persist_period_sec=600 + enforce_single_del_contracts=true + lowest_used_cache_tier=kNonVolatileBlockTier + bgerror_resume_retry_interval=1000000 + best_efforts_recovery=false + log_readahead_size=0 + write_dbid_to_manifest=false + wal_compression=kNoCompression + manual_wal_flush=false + db_host_id=__hostname__ + two_write_queues=false + random_access_max_buffer_size=1048576 + avoid_unnecessary_blocking_io=false + skip_checking_sst_file_sizes_on_db_open=false + flush_verify_memtable_count=true + fail_if_options_file_error=true + atomic_flush=false + verify_sst_unique_id_in_manifest=true + skip_stats_update_on_db_open=false + track_and_verify_wals_in_manifest=false + compaction_verify_record_count=true + paranoid_checks=true + create_if_missing=true + max_write_batch_group_size_bytes=1048576 + avoid_flush_during_recovery=false + file_checksum_gen_factory=nullptr + enable_thread_tracking=false + allow_fallocate=true + allow_data_in_errors=false + error_if_exists=false + use_direct_io_for_flush_and_compaction=false + create_missing_column_families=true + WAL_size_limit_MB=512 + use_direct_reads=false + persist_stats_to_disk=false + allow_2pc=true + is_fd_close_on_exec=true + max_log_file_size=0 + access_hint_on_compaction_start=NORMAL + max_file_opening_threads=16 + wal_filter=nullptr + allow_mmap_reads=false + allow_mmap_writes=false + use_adaptive_mutex=false + use_fsync=true + table_cache_numshardbits=6 + dump_malloc_stats=false + db_write_buffer_size=0 + allow_ingest_behind=false + keep_log_file_num=1000 + max_bgerror_resume_count=2147483647 + allow_concurrent_memtable_write=true + recycle_log_file_num=0 + log_file_time_to_roll=0 + manifest_preallocation_size=4194304 + enable_write_thread_adaptive_yield=true + WAL_ttl_seconds=0 + max_manifest_file_size=1073741824 + wal_recovery_mode=kPointInTimeRecovery + enable_pipelined_write=false + write_thread_slow_yield_usec=3 + unordered_write=false + write_thread_max_yield_usec=100 + advise_random_on_open=true + info_log_level=INFO_LEVEL + + +[CFOptions "default"] + memtable_max_range_deletions=0 + compression_opts={checksum=false;max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;max_compressed_bytes_per_kb=896;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} + block_protection_bytes_per_key=0 + bottommost_file_compaction_delay=0 + memtable_protection_bytes_per_key=0 + target_file_size_multiplier=1 + report_bg_io_stats=false + write_buffer_size=534217728 + memtable_huge_page_size=0 + max_successive_merges=0 + max_write_buffer_number=50 + prefix_extractor=rocksdb.CappedPrefix.9 + bottommost_compression_opts={checksum=false;max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;max_compressed_bytes_per_kb=896;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} + paranoid_file_checks=false + blob_garbage_collection_force_threshold=1.000000 + enable_blob_files=true + blob_file_starting_level=0 + memtable_prefix_bloom_size_ratio=0.000000 + inplace_update_num_locks=10000 + blob_compaction_readahead_size=0 + ignore_max_compaction_bytes_for_input=true + arena_block_size=1048576 + level0_stop_writes_trigger=36 + blob_compression_type=kNoCompression + level0_slowdown_writes_trigger=20 + hard_pending_compaction_bytes_limit=274877906944 + soft_pending_compaction_bytes_limit=68719476736 + target_file_size_base=67108864 + level0_file_num_compaction_trigger=16 + max_compaction_bytes=2677721600 + disable_auto_compactions=false + check_flush_compaction_key_order=true + min_blob_size=0 + memtable_whole_key_filtering=false + max_bytes_for_level_base=268435456 + last_level_temperature=kUnknown + compaction_options_fifo={file_temperature_age_thresholds=;allow_compaction=false;age_for_warm=0;max_table_files_size=1073741824;} + max_bytes_for_level_multiplier=10.000000 + max_bytes_for_level_multiplier_additional=1:1:1:1:1:1:1 + max_sequential_skip_in_iterations=8 + prepopulate_blob_cache=kDisable + compression=kNoCompression + compaction_options_universal={incremental=false;compression_size_percent=-1;allow_trivial_move=false;max_size_amplification_percent=200;max_merge_width=4294967295;stop_style=kCompactionStopStyleTotalSize;min_merge_width=2;size_ratio=1;} + blob_garbage_collection_age_cutoff=0.250000 + ttl=2592000 + periodic_compaction_seconds=0 + sample_for_compression=0 + blob_file_size=268435456 + enable_blob_garbage_collection=false + experimental_mempurge_threshold=0.000000 + bottommost_compression=kZSTD + persist_user_defined_timestamps=true + preserve_internal_time_seconds=0 + preclude_last_level_data_seconds=0 + sst_partitioner_factory=nullptr + num_levels=7 + force_consistency_checks=true + memtable_insert_with_hint_prefix_extractor=nullptr + memtable_factory=SkipListFactory + compaction_pri=kMinOverlappingRatio + max_write_buffer_size_to_maintain=534217728 + level_compaction_dynamic_file_size=true + max_write_buffer_number_to_maintain=0 + optimize_filters_for_hits=false + level_compaction_dynamic_level_bytes=true + default_temperature=kUnknown + inplace_update_support=false + merge_operator=nullptr + table_factory=BlockBasedTable + min_write_buffer_number_to_merge=4 + compaction_filter=nullptr + compaction_style=kCompactionStyleLevel + bloom_locality=0 + comparator=leveldb.BytewiseComparator + compaction_filter_factory=nullptr + +[TableOptions/BlockBasedTable "default"] + initial_auto_readahead_size=8192 + pin_top_level_index_and_filter=true + block_align=false + block_size_deviation=10 + checksum=kXXH3 + index_shortening=kShortenSeparators + num_file_reads_for_auto_readahead=2 + whole_key_filtering=true + data_block_index_type=kDataBlockBinarySearch + index_type=kBinarySearch + no_block_cache=false + index_block_restart_interval=1 + data_block_hash_table_util_ratio=0.750000 + prepopulate_block_cache=kDisable + pin_l0_filter_and_index_blocks_in_cache=false + filter_policy=bloomfilter:9.9:false + cache_index_and_filter_blocks_with_high_priority=true + verify_compression=false + block_restart_interval=16 + max_auto_readahead_size=262144 + flush_block_policy_factory=FlushBlockBySizePolicyFactory + partition_filters=false + cache_index_and_filter_blocks=false + block_size=4096 + metadata_block_size=4096 + optimize_filters_for_memory=false + detect_filter_construct_corruption=false + format_version=5 + metadata_cache_options={unpartitioned_pinning=kFallback;partition_pinning=kFallback;top_level_index_pinning=kFallback;} + read_amp_bytes_per_bit=0 + enable_index_compression=true + diff --git a/memory-store/run.sh b/memory-store/run.sh index 838280452..bdfc2d140 100755 --- a/memory-store/run.sh +++ b/memory-store/run.sh @@ -2,8 +2,8 @@ set -eo pipefail -# Create mount directory for service. -mkdir -p ${COZO_MNT_DIR:=/data} +# Create mount directory for service and RocksDB directory +mkdir -p ${COZO_MNT_DIR:=/data}/${COZO_ROCKSDB_DIR:-cozo.db} # Create auth token if not exists. export COZO_AUTH_TOKEN=${COZO_AUTH_TOKEN:=`tr -dc A-Za-z0-9 $COZO_MNT_DIR/${COZO_ROCKSDB_DIR}.rocksdb.cozo_auth +# Copy options file to the RocksDB directory +cp /app/options $COZO_MNT_DIR/${COZO_ROCKSDB_DIR}/options + # Start server ${APP_HOME:=.}/bin/cozo server \ --engine rocksdb \ diff --git a/monitoring/README.md b/monitoring/README.md index 9702bf6ff..3366abcd7 100644 --- a/monitoring/README.md +++ b/monitoring/README.md @@ -1,41 +1,28 @@ -# TODO: Fix monitoring services -# SCRUM-25 - # Compose sample ### Prometheus & Grafana Project structure: ``` -. -├── compose.yaml +├── README.md +├── docker-compose.yml ├── grafana -│   └── datasource.yml -├── prometheus -│   └── prometheus.yml -└── README.md +│ └── provisioning +│ └── datasources +│ └── datasource.yml +└── prometheus + └── config + └── prometheus.yml ``` -[_compose.yaml_](compose.yaml) -``` -services: - prometheus: - image: prom/prometheus - ... - ports: - - 9090:9090 - grafana: - image: grafana/grafana - ... - ports: - - 3000:3000 -``` -The compose file defines a stack with two services `prometheus` and `grafana`. +The docker compose file defines a stack with two services `prometheus` and `grafana`. When deploying the stack, docker compose maps port the default ports for each service to the equivalent ports on the host in order to inspect easier the web interface of each service. Make sure the ports 9090 and 3000 on the host are not already in use. ## Deploy with docker compose ``` +$ docker volume create grafana_data +$ docker volume create prometheus_data $ docker compose up -d Creating network "prometheus-grafana_default" with the default driver Creating volume "prometheus-grafana_prom_data" with default driver diff --git a/monitoring/docker-compose.yml b/monitoring/docker-compose.yml index 51cb60f67..72edabf48 100644 --- a/monitoring/docker-compose.yml +++ b/monitoring/docker-compose.yml @@ -1,39 +1,39 @@ name: julep-monitoring services: - portainer: - image: portainer/portainer-ce:latest - container_name: portainer - restart: unless-stopped - ports: - - "9000:8000" - - "9443:9443" - volumes: - - portainer_data:/data - - /var/run/docker.sock:/var/run/docker.sock - prometheus: image: prom/prometheus container_name: prometheus - command: - - "--config.file=/etc/prometheus/prometheus.yml" - restart: unless-stopped + profiles: + - multi-tenant + volumes: - - ./prometheus:/etc/prometheus - - prom_data:/prometheus + - ./prometheus/config/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + + depends_on: + agents-api-multi-tenant: + condition: service_started + + command: + - '--config.file=/etc/prometheus/prometheus.yml' grafana: image: grafana/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} + - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER} container_name: grafana ports: - 3000:3000 - restart: unless-stopped - environment: - - GF_SECURITY_ADMIN_USER=admin - - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} volumes: - - ./grafana:/etc/grafana/provisioning/datasources + - grafana_data:/var/lib/grafana + - ./grafana/provisioning:/etc/grafana/provisioning + profiles: + - multi-tenant volumes: - prom_data: - portainer_data: + prometheus_data: + external: true + grafana_data: + external: true diff --git a/monitoring/grafana/datasource.yml b/monitoring/grafana/datasource.yml deleted file mode 100644 index d7b828686..000000000 --- a/monitoring/grafana/datasource.yml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: 1 - -datasources: -- name: Prometheus - type: prometheus - url: http://prometheus:9090 - isDefault: true - access: proxy - editable: true diff --git a/monitoring/grafana/provisioning/datasources/datasource.yml b/monitoring/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 000000000..8341f7670 --- /dev/null +++ b/monitoring/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,9 @@ +datasources: +- access: 'proxy' + editable: true + is_default: true + name: 'agents-api' + org_id: 1 + type: 'prometheus' + url: 'http://prometheus:9090' + version: 1 diff --git a/monitoring/prometheus/config/prometheus.yml b/monitoring/prometheus/config/prometheus.yml new file mode 100644 index 000000000..e556a2c53 --- /dev/null +++ b/monitoring/prometheus/config/prometheus.yml @@ -0,0 +1,24 @@ +global: + scrape_interval: 15s + scrape_timeout: 10s + evaluation_interval: 15s +alerting: + alertmanagers: + - follow_redirects: true + enable_http2: true + scheme: http + timeout: 10s + api_version: v2 + static_configs: + - targets: [] +scrape_configs: +- job_name: agents-api + honor_timestamps: true + scrape_interval: 15s + scrape_timeout: 10s + metrics_path: /metrics + scheme: http + follow_redirects: true + static_configs: + - targets: + - agents-api-multi-tenant:8080 diff --git a/monitoring/prometheus/prometheus.yml b/monitoring/prometheus/prometheus.yml deleted file mode 100644 index dec073bbb..000000000 --- a/monitoring/prometheus/prometheus.yml +++ /dev/null @@ -1,21 +0,0 @@ -global: - scrape_interval: 5s - scrape_timeout: 3s - evaluation_interval: 5s -alerting: - alertmanagers: - - static_configs: - - targets: [] - scheme: http - timeout: 3s - api_version: v1 -scrape_configs: - - job_name: traefik - honor_timestamps: true - scrape_interval: 5s - scrape_timeout: 3s - metrics_path: /metrics - scheme: http - static_configs: - - targets: - - gateway:8082 diff --git a/openapi.yaml b/openapi.yaml index f381c1085..9bb592e6d 120000 --- a/openapi.yaml +++ b/openapi.yaml @@ -1 +1 @@ -typespec/tsp-output/@typespec/openapi3/openapi-0.4.0.yaml \ No newline at end of file +typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml \ No newline at end of file diff --git a/scheduler/docker-compose.yml b/scheduler/docker-compose.yml index e0f86ec9d..67eda6f6f 100644 --- a/scheduler/docker-compose.yml +++ b/scheduler/docker-compose.yml @@ -1,7 +1,7 @@ name: julep-scheduler x--temporal-base: &temporal-base - image: temporalio/auto-setup:1.25 + image: temporalio/auto-setup:1.25.2 hostname: temporal environment: - POSTGRES_PWD=${TEMPORAL_POSTGRES_PASSWORD} @@ -43,6 +43,7 @@ services: temporal-db: image: postgres:16 restart: unless-stopped + command: -c 'max_connections=1000' volumes: - temporal-db-data:/var/lib/postgresql/data profiles: @@ -53,7 +54,7 @@ services: - POSTGRES_PASSWORD=${TEMPORAL_POSTGRES_PASSWORD} healthcheck: test: [ "CMD-SHELL", "pg_isready -d ${TEMPORAL_POSTGRES_DB:-temporal} -U ${TEMPORAL_POSTGRES_USER:-temporal}" ] - + interval: 1s timeout: 5s retries: 10 @@ -63,11 +64,48 @@ services: profiles: - temporal-ui environment: + # See: https://github.com/temporalio/ui-server/blob/main/docker/config-template.yaml - TEMPORAL_ADDRESS=${TEMPORAL_ADDRESS:-temporal:7233} - - TEMPORAL_CORS_ORIGINS=${TEMPORAL_CORS_ORIGINS:-http://localhost:3000} + # Note: Not setting it enables all origins + # - TEMPORAL_CORS_ORIGINS=${TEMPORAL_CORS_ORIGINS:-http://localhost:3000} + - TEMPORAL_CODEC_ENDPOINT=http://localhost/api/temporal + - TEMPORAL_UI_ENABLED=true + - TEMPORAL_FEEDBACK_URL=https://github.com/julep-ai/julep + - TEMPORAL_NOTIFY_ON_NEW_VERSION=false + - TEMPORAL_CSRF_COOKIE_INSECURE=true + # - TEMPORAL_HIDE_LOGS=true + # - TEMPORAL_BANNER_TEXT=gagagaga + # - TEMPORAL_DISABLE_WRITE_ACTIONS=true + # - TEMPORAL_HIDE_WORKFLOW_QUERY_ERRORS=true + # - TEMPORAL_REFRESH_WORKFLOW_COUNTS_DISABLED=true + - TEMPORAL_OPEN_API_ENABLED=true + ports: - 9000:8080 # Since 8080 is already used by agents-api + temporal-ui-public: + image: temporalio/ui:latest + profiles: + - temporal-ui-public + environment: + # See: https://github.com/temporalio/ui-server/blob/main/docker/config-template.yaml + - TEMPORAL_ADDRESS=${TEMPORAL_ADDRESS:-temporal:7233} + - TEMPORAL_CODEC_ENDPOINT=http://localhost/api/temporal + - TEMPORAL_UI_ENABLED=true + - TEMPORAL_FEEDBACK_URL=https://github.com/julep-ai/julep + - TEMPORAL_NOTIFY_ON_NEW_VERSION=false + - TEMPORAL_CSRF_COOKIE_INSECURE=true + - TEMPORAL_DISABLE_WRITE_ACTIONS=true + - TEMPORAL_OPEN_API_ENABLED=true + - TEMPORAL_UI_BASE_PATH=/tasks-ui + - TEMPORAL_UI_PUBLIC_PATH=/tasks-ui + + + ports: + - 9001:8080 # Since 8080 is already used by agents-api + + volumes: temporal-db-data: + external: true diff --git a/scheduler/dynamicconfig/temporal-postgres.yaml b/scheduler/dynamicconfig/temporal-postgres.yaml index e9902c070..5d52ad979 100644 --- a/scheduler/dynamicconfig/temporal-postgres.yaml +++ b/scheduler/dynamicconfig/temporal-postgres.yaml @@ -4,3 +4,40 @@ limit.maxIDLength: system.forceSearchAttributesCacheRefreshOnRead: - value: false constraints: {} + +matching.rps: + - value: 600 # default 1200 + constraints: {} + +limit.blobSize.error: + - value: 4194304 # default 2*1024*1024 + constraints: {} + +limit.historySize.error: + - value: 104857600 # default 50*1024*1024 + constraints: {} + +limit.historyCount.error: + - value: 102400 # default 50*1024 + constraints: {} + +limit.numPendingActivities.error: + - value: 4000 # default 2000 + constraints: {} + +limit.numPendingChildExecutions.error: + - value: 4000 # default 2000 + constraints: {} + +frontend.MaxExecutionCountBatchOperationPerNamespace: + - value: 10000 + constraints: {} + +frontend.MaxConcurrentBatchOperationPerNamespace: + - value: 100 + constraints: {} + +# worker.ScannerMaxConcurrentWorkflowTaskExecutionSize (10 default), worker.ParentCloseMaxConcurrentWorkflowTaskExecutionSize (1000 default) (reduce?) + + +# worker.ScannerMaxConcurrentActivityExecutionSize (10 default), worker.ParentCloseMaxConcurrentActivityExecutionSize (1000 default) (reduce?) diff --git a/scripts/generate_openapi_code.sh b/scripts/generate_openapi_code.sh index f3cfe70b1..ebb080607 100755 --- a/scripts/generate_openapi_code.sh +++ b/scripts/generate_openapi_code.sh @@ -15,3 +15,9 @@ cd agents-api && \ poetry run poe codegen && \ poetry run poe format cd - + +cd integrations-service && \ + # poetry update && \ + poetry run poe codegen && \ + poetry run poe format +cd - diff --git a/scripts/readme_translator.py b/scripts/readme_translator.py new file mode 100644 index 000000000..180c69d9a --- /dev/null +++ b/scripts/readme_translator.py @@ -0,0 +1,91 @@ +import re +import logging +from typing import List +from pathlib import Path +from functools import partial +from deep_translator import GoogleTranslator +import parmapper + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +HTML_TAGS_PATTERN = r"(<[^>]+>)" +CODEBLOCK_PATTERN = r"(```[\s\S]*?```|\n)" + +def create_translator(target: str) -> GoogleTranslator: + """ + Create a translator for a given target language. + """ + return GoogleTranslator(source="en", target=target) + +def is_html_tag(segment: str) -> bool: + """Check if the segment is an HTML tag.""" + return re.fullmatch(HTML_TAGS_PATTERN, segment) is not None + +def is_special_character(segment: str) -> bool: + """Check if the segment consists of special characters only.""" + return re.fullmatch(r'^[!"#$%&\'()*+,\-./:;<=>?@[\]^_`{|}~]+$', segment) is not None + +def translate_sub_segment(translator: GoogleTranslator, sub_segment: str) -> str: + """Translate a single sub-segment.""" + try: + translated = translator.translate(sub_segment) + return translated if translated else sub_segment + except Exception as e: + logging.error(f"Error translating segment '{sub_segment}': {e}") + return sub_segment + +def translate_segment(translator: GoogleTranslator, segment: str) -> str: + """ + Translate a given raw HTML content using the provided translator, preserving HTML tags and newlines. + """ + if re.fullmatch(CODEBLOCK_PATTERN, segment) or segment == '\n': + return segment + + segments = re.split(HTML_TAGS_PATTERN, segment) + translated_segments = [] + + for sub_segment in segments: + if is_html_tag(sub_segment): + translated_segments.append(sub_segment) + elif is_special_character(sub_segment): + translated_segments.append(sub_segment) + else: + translated_segments.append(translate_sub_segment(translator, sub_segment)) + + return "".join(translated_segments) + +def translate_readme(source: str, target: str) -> str: + """ + Translate a README file from source to target language, preserving code blocks and newlines. + """ + file_content = Path(source).read_text(encoding='utf-8') + translator = create_translator(target) + segments = re.split(CODEBLOCK_PATTERN, file_content) + segment_translation = partial(translate_segment, translator) + translated_segments = list(parmapper.parmap(segment_translation, segments)) + return ''.join(translated_segments) + +def save_translated_readme(translated_content: str, lang: str) -> None: + """ + Save the translated README content to a file. + """ + filename = f"README-{lang.split('-')[-1].upper()}.md" + with open(filename, "w", encoding='utf-8') as file: + file.write(translated_content) + +def main() -> None: + """ + Main function to translate README.md to multiple languages. + """ + source_file = "README.md" + destination_langs = ["zh-CN", "ja", "fr"] + + for lang in destination_langs: + logging.info(f"Translating to {lang}...") + translated_readme = translate_readme(source_file, lang) + save_translated_readme(translated_readme, lang) + logging.info(f"Saved translated README for {lang}.") + +if __name__ == "__main__": + main() diff --git a/sdks/node-sdk b/sdks/node-sdk index 2685cfe51..6cb742bba 160000 --- a/sdks/node-sdk +++ b/sdks/node-sdk @@ -1 +1 @@ -Subproject commit 2685cfe512d6b2907e6bdd1b3294175e20aece99 +Subproject commit 6cb742bba2b408ef2ea070bfe284595bcdb974fe diff --git a/sdks/python-sdk b/sdks/python-sdk index aaa88a204..7f9bc0c59 160000 --- a/sdks/python-sdk +++ b/sdks/python-sdk @@ -1 +1 @@ -Subproject commit aaa88a204bb85b7903f79b8fb5cca0c3e6882c73 +Subproject commit 7f9bc0c59d2e80f6e707f5dcc9e78fecd197c3ca diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index 2b2fea34f..1e64144a2 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -159,7 +159,7 @@ model ChatInputData { messages: InputChatMLMessage[]; /** (Advanced) List of tools that are provided in addition to agent's default set of tools. */ - tools: Tool[] = #[]; + tools: CreateToolRequest[] = #[]; /** Can be one of existing tools given to the agent earlier or the ones provided in this request. */ tool_choice?: ToolChoiceOption; @@ -199,6 +199,9 @@ model BaseChatOutput { /** The log probabilities of tokens */ logprobs?: LogProbResponse; + + /** The tool calls generated by the model */ + tool_calls?: ChosenToolCall[]; } /** The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ diff --git a/typespec/common/constants.tsp b/typespec/common/constants.tsp index e60329752..bcd9e8bc1 100644 --- a/typespec/common/constants.tsp +++ b/typespec/common/constants.tsp @@ -10,7 +10,7 @@ You are {{agent.name}}.{{" "}} {%- endif -%} {%- if agent.about -%} -About you: {{agent.name}}.{{" "}} +About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -20,42 +20,38 @@ You are talking to a user {%- endif -%} {%- endif -%} -{{"\n\n"}} +{{NEWLINE+NEWLINE}} {%- if agent.instructions -%} -Instructions:{{"\n"}} +Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{"\n"}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{"\n"}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{"\n"}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} -Tools:{{"\n"}} +Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} -{{"\n\n"}} +{{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} -Relevant documents:{{"\n"}} +Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{"\n"}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{"\n"}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{"\n"}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} diff --git a/typespec/common/interfaces.tsp b/typespec/common/interfaces.tsp index d9e4a9e2e..7b719418c 100644 --- a/typespec/common/interfaces.tsp +++ b/typespec/common/interfaces.tsp @@ -1,9 +1,11 @@ import "@typespec/http"; +import "@typespec/sse"; import "./scalars.tsp"; import "./types.tsp"; using TypeSpec.Http; +using TypeSpec.SSE; namespace Common; @@ -140,28 +142,6 @@ interface ChildLimitOffsetPagination< }; } -interface ChildStreamEndpoint< - T, - DocString extends valueof string = "Stream events emitted by the parent" -> { - @get - @doc(DocString) - stream( - @path - @doc("ID of parent") - id: uuid, - - @query - @doc("Next page token") - next_token: string | null = null, - ): { - @header contentType: eventStream; - - @body - @doc("Stream of events emitted by the parent") - body: T; - }; -} interface ChildCreateEndpoint< CreateType, @@ -274,3 +254,20 @@ interface ChildPatchEndpoint< body: ResourceUpdatedResponse; }; } + +interface ChildStreamEndpoint< + T, + DocString extends valueof string = "Stream events emitted by the parent" +> { + @get + @doc(DocString) + stream( + @path + @doc("ID of parent") + id: uuid, + + @query + @doc("Next page token") + next_token: string | null = null, + ): SSEStream>; +} \ No newline at end of file diff --git a/typespec/common/scalars.tsp b/typespec/common/scalars.tsp index 95c92d0b8..c718f6289 100644 --- a/typespec/common/scalars.tsp +++ b/typespec/common/scalars.tsp @@ -18,6 +18,11 @@ alias concreteType = numeric | string | boolean | null; @pattern("^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$") scalar identifierSafeUnicode extends string; +/** Valid mime types */ +@maxLength(120) +@pattern("^(application|audio|font|example|image|message|model|multipart|text|video|x-(?:[0-9A-Za-z!#$%&'*+.^_`|~-]+))\\/([0-9A-Za-z!#$%&'*+.^_`|~-]+)$") +scalar mimeType extends string; + /** Valid python identifier names */ @pattern("^[^\\W0-9]\\w*$") @maxLength(40) @@ -48,7 +53,13 @@ alias json = "application/json"; alias eventStream = "text/event-stream"; /** Different possible sources that can produce new entries */ -alias entrySource = "api_request" | "api_response" | "tool_response" | "internal" | "summarizer" | "meta"; +alias entrySource = + | "api_request" + | "api_response" + | "tool_response" + | "internal" + | "summarizer" + | "meta"; /** A simple python expression compatible with SimpleEval. */ scalar PyExpression extends string; diff --git a/typespec/common/types.tsp b/typespec/common/types.tsp index dd331e98c..40071866b 100644 --- a/typespec/common/types.tsp +++ b/typespec/common/types.tsp @@ -1,6 +1,8 @@ +import "@typespec/events"; import "@typespec/http"; import "@typespec/openapi"; +using TypeSpec.Events; using TypeSpec.Http; using TypeSpec.OpenAPI; @@ -11,7 +13,7 @@ namespace Common; // alias Metadata = Record; -alias MetadataFilter = Record; +alias MetadataFilter = Record; model ResourceCreatedResponse { @doc("ID of created resource") @@ -50,5 +52,10 @@ model PaginationOptions { @query direction: sortDirection = "asc", /** Object to filter results by metadata */ - @query metadata_filter: MetadataFilter, -} \ No newline at end of file + @query metadata_filter: Record = #{}, +} + +@events +union StreamEvent { + T; +} diff --git a/typespec/docs/models.tsp b/typespec/docs/models.tsp index f01b379c6..055fc2003 100644 --- a/typespec/docs/models.tsp +++ b/typespec/docs/models.tsp @@ -33,6 +33,9 @@ model Doc { @withVisibility("create") model CreateDocRequest { ...Doc; + + /** Instruction for the embedding model. */ + embed_instruction: string | null = null; } model DocOwner { @@ -43,9 +46,12 @@ model DocOwner { model Snippet { index: uint16; content: string; + embedding?: float[]; } model DocReference { + ...HasMetadata; + /** The owner of this document. */ owner: DocOwner; @@ -53,17 +59,31 @@ model DocReference { id: Doc.id; title?: string; - - @minItems(1) - snippets: Snippet[]; + + snippet: Snippet; distance: float | null = null; } -model EmbedQueryRequest { - /** Text or texts to embed */ - text: string | string[]; +model SingleEmbedQueryRequest { + /** Text to embed */ + text: string; + + /** Instruction for the embedding model. */ + embed_instruction: string = ""; } +model MultipleEmbedQueryRequest { + /** Texts to embed */ + @minItems(1) + @maxItems(100) + text: string[]; + + /** Instruction for the embedding model. */ + embed_instruction: string = ""; +} + +alias EmbedQueryRequest = SingleEmbedQueryRequest | MultipleEmbedQueryRequest; + model EmbedQueryResponse { /** The embedded vectors */ vectors: float[][]; @@ -71,11 +91,17 @@ model EmbedQueryResponse { model BaseDocSearchRequest { @minValue(1) - @maxValue(100) + @maxValue(50) limit: uint16 = 10; /** The language to be used for text-only search. Support for other languages coming soon. */ lang: "en-US" = "en-US"; + metadata_filter: MetadataFilter = #{}, + + /** MMR Strength (mmr_strength = 1 - mmr_lambda) */ + @minValue(0) + @maxValueExclusive(1) + mmr_strength?: float = 0.0; } model VectorDocSearchRequest extends BaseDocSearchRequest { @@ -88,6 +114,8 @@ model VectorDocSearchRequest extends BaseDocSearchRequest { vector: float[]; text?: never; + + mmr_strength?: never; } model TextOnlyDocSearchRequest extends BaseDocSearchRequest { diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index 0b46af0a3..7f8c8b9fa 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -19,15 +19,12 @@ enum ImageDetail { auto, } -/** ChatML role (system|assistant|user|function_call|function|function_response|auto) */ +/** ChatML role (system|assistant|user|tool) */ enum ChatMLRole { user, assistant, system, - function, - function_response, - function_call, - auto, + tool, } model ImageURL { @@ -53,14 +50,33 @@ model ChatMLImageContentPart { type: "image_url" = "image_url"; } -alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart; +model ChatMLAnthropicImageSource { + type: "base64" = "base64"; + media_type: string; + data: T; +} + +model ChatMLAnthropicImageContentPart { + type: "image" = "image"; + source: ChatMLAnthropicImageSource; +} + +/** Anthropic image content part */ +model ChatMLAnthropicContentPart { + tool_use_id: string; + type: "tool_result" = "tool_result"; + content: ChatMLTextContentPart[] | ChatMLAnthropicImageContentPart[]; +} + +alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart | ChatMLAnthropicContentPart; model ChatMLMessage { /** The role of the message */ role: ChatMLRole; + tool_call_id?: string; /** The content parts of the message */ - content: T | T[] | ChatMLContentPart[]; + content: T | T[] | ChatMLContentPart[] | null; /** Name */ name?: string; @@ -70,7 +86,6 @@ model ChatMLMessage { continue?: boolean; /** Tool calls generated by the model. */ - @visibility("read") tool_calls?: ChosenToolCall[] | null = #[]; ...HasCreatedAtOptional; @@ -93,6 +108,12 @@ model BaseEntry { tokenizer: string; token_count: uint16; + /** Tool calls generated by the model. */ + tool_calls?: ChosenToolCall[] | null = null; + + /** The tool call id of the tool call this message is a response to */ + tool_call_id?: string | null = null; + /** This is the time that this event refers to. */ @minValue(0) timestamp: float; @@ -116,4 +137,4 @@ model History { session_id: Session.id; ...HasCreatedAt; -} +} \ No newline at end of file diff --git a/typespec/files/endpoints.tsp b/typespec/files/endpoints.tsp new file mode 100644 index 000000000..787f1b1c9 --- /dev/null +++ b/typespec/files/endpoints.tsp @@ -0,0 +1,14 @@ +import "../common"; +import "./models.tsp"; + +using Common; + +namespace Files; + +// +// FILE ENDPOINTS +// + +interface Endpoints + extends GetEndpoint, + CreateEndpoint {} diff --git a/typespec/files/main.tsp b/typespec/files/main.tsp new file mode 100644 index 000000000..f60022763 --- /dev/null +++ b/typespec/files/main.tsp @@ -0,0 +1,8 @@ +import "./endpoints.tsp"; +import "./models.tsp"; + +namespace Files; + +// +// FILES +// diff --git a/typespec/files/models.tsp b/typespec/files/models.tsp new file mode 100644 index 000000000..943b64241 --- /dev/null +++ b/typespec/files/models.tsp @@ -0,0 +1,41 @@ +import "../common"; + +using Common; + +namespace Files; + +// +// FILE MODELS +// + +model File { + ...HasId; + ...HasCreatedAt; + + /** Name of the file */ + name: identifierSafeUnicode; + + /** Description of the file */ + description: string = ""; + + /** MIME type of the file */ + mime_type: mimeType | null = null; + + /** Base64 encoded content of the file */ + content: string; + + /** Size of the file in bytes */ + @visibility("read") + @minValue(1) + size: uint64; + + /** Hash of the file */ + @visibility("read") + hash: string; +} + +/** Payload for creating a file */ +@withVisibility("create") +model CreateFileRequest { + ...File; +} diff --git a/typespec/main.tsp b/typespec/main.tsp index b3bb1278c..3ec3739dd 100644 --- a/typespec/main.tsp +++ b/typespec/main.tsp @@ -7,6 +7,7 @@ import "./chat"; import "./docs"; import "./entries"; import "./executions"; +import "./files"; import "./jobs"; import "./sessions"; import "./tasks"; @@ -95,6 +96,9 @@ namespace Api { @route("/docs") interface IndividualDocsRoute extends Docs.IndividualDocEndpoints {} + @route("/tasks") + interface TasksGetRoute extends Tasks.GetEndpoints {} + @route("/agents/{id}/tasks") interface TasksRoute extends Tasks.Endpoints {} @@ -115,4 +119,7 @@ namespace Api { @route("/jobs") interface JobRoute extends Jobs.Endpoints {} + + @route("/files") + interface FilesRoute extends Files.Endpoints {} } diff --git a/typespec/package-lock.json b/typespec/package-lock.json index 0ddfdb155..80ad055ad 100644 --- a/typespec/package-lock.json +++ b/typespec/package-lock.json @@ -8,12 +8,14 @@ "name": "julep-typespec", "version": "0.4.0", "dependencies": { - "@typespec/compiler": "^0.60.1", - "@typespec/http": "^0.60.0", - "@typespec/openapi": "^0.60.0", - "@typespec/openapi3": "^0.60.0", - "@typespec/rest": "^0.60.0", - "@typespec/versioning": "^0.60.1" + "@typespec/compiler": "^0.61.2", + "@typespec/events": "^0.61.0", + "@typespec/http": "^0.61.0", + "@typespec/openapi": "^0.61.0", + "@typespec/openapi3": "^0.61.0", + "@typespec/rest": "^0.61.0", + "@typespec/sse": "^0.61.0", + "@typespec/versioning": "^0.61.0" } }, "node_modules/@apidevtools/swagger-methods": { @@ -274,9 +276,9 @@ "license": "MIT" }, "node_modules/@typespec/compiler": { - "version": "0.60.1", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.60.1.tgz", - "integrity": "sha512-I6Vcpvd7mBP7SI5vCBh9rZGXAtVy95BKhAd33Enw32psswiSzRpA7zdyZhOMekTOGVXNS/+E5l2PGGCzQddB4w==", + "version": "0.61.2", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.61.2.tgz", + "integrity": "sha512-6QxYJd09VWssd/BvY+8eBxTVv085s1UNK63FdPrgT2lgI+j8VMMcpNR9m5l1zWlgGDM7sniA/Or8VCdVA6jerg==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.24.7", @@ -284,14 +286,14 @@ "change-case": "~5.4.4", "globby": "~14.0.2", "mustache": "~4.2.0", - "picocolors": "~1.0.1", + "picocolors": "~1.1.0", "prettier": "~3.3.3", "prompts": "~2.4.2", "semver": "^7.6.3", "temporal-polyfill": "^0.2.5", "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "yaml": "~2.4.5", + "vscode-languageserver-textdocument": "~1.0.12", + "yaml": "~2.5.1", "yargs": "~17.7.2" }, "bin": { @@ -302,39 +304,57 @@ "node": ">=18.0.0" } }, + "node_modules/@typespec/events": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.61.0.tgz", + "integrity": "sha512-XUXy36qGo7v1ZBEK5WTD3TGXc4xr9rbL5U5f7aCabad4YHTi6r/2GMOVjuRJOiCJoMvEVeL1pWkhDZkBPbdd3A==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "~0.61.0" + } + }, "node_modules/@typespec/http": { - "version": "0.60.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.60.0.tgz", - "integrity": "sha512-ktfS9vpHfltyeAaQLNAZdqrn6Per3vmB/HDH/iyudYLA5wWblT1siKvpFCMWq53CJorRO7yeOKv+Q/M26zwEtg==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.61.0.tgz", + "integrity": "sha512-7+AYHkzkc+p652GY9BcEbXY4OZa1fTr03MVmZeafvmbQbXfyzUU9eJld13M3v6NaUWqXWZ7nBNMISyKiXp/kSw==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.60.0" + "@typespec/compiler": "~0.61.0", + "@typespec/streams": "~0.61.0" + }, + "peerDependenciesMeta": { + "@typespec/streams": { + "optional": true + } } }, "node_modules/@typespec/openapi": { - "version": "0.60.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.60.0.tgz", - "integrity": "sha512-YVwLppgHY8r/MudHNSLSUXzdw+CIpjmb31gI2a0KDGnI6sWDwY7LSWfjGU4TY/ubt0+X0Tjoy330mTvw71YBTg==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.61.0.tgz", + "integrity": "sha512-3AF319Ae4yGVOscsCLQeedXUJJcL/NdGOR2/e/nFiL/AOVdgLfIRnpR0Ad9Zj9XAESh1fq9XSu4Mi7N1k4V7rw==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.60.0", - "@typespec/http": "~0.60.0" + "@typespec/compiler": "~0.61.0", + "@typespec/http": "~0.61.0" } }, "node_modules/@typespec/openapi3": { - "version": "0.60.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.60.0.tgz", - "integrity": "sha512-gvrTHZACdeQtV7GfhVOHqkyTgMFyM2nKAIiz2P83LIncMCDUc00bGKGmaBk+xpuwKtCJyxBeVpCbID31YAq96g==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.61.0.tgz", + "integrity": "sha512-ALLsTkK1UiJBzvygV1Zk/yZaym+lOWroGeEUhQNXYShsq+/GLZkK0rl8sd76Gigq+TVXKMOEwUUvgfws/LMUJw==", "license": "MIT", "dependencies": { "@readme/openapi-parser": "~2.6.0", - "yaml": "~2.4.5" + "yaml": "~2.5.1" }, "bin": { "tsp-openapi3": "cmd/tsp-openapi3.js" @@ -343,35 +363,63 @@ "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.60.0", - "@typespec/http": "~0.60.0", - "@typespec/openapi": "~0.60.0", - "@typespec/versioning": "~0.60.0" + "@typespec/compiler": "~0.61.0", + "@typespec/http": "~0.61.0", + "@typespec/openapi": "~0.61.0", + "@typespec/versioning": "~0.61.0" } }, "node_modules/@typespec/rest": { - "version": "0.60.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.60.0.tgz", - "integrity": "sha512-mHYubyuBvwdV2xkHrJfPwV7b/Ksyb9lA1Q/AQwpVFa7Qu1X075TBVALmH+hK3V0EdUG1CGJZ5Sw4BWgl8ZS0BA==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.61.0.tgz", + "integrity": "sha512-L9Oyor+l42p6S8GE+UvaZTi+dcu6WubGZKmaBRpX8mCZGsa69EgIK8DQoyxrfMcxAO4I5U0sfkzCKwCVFtRr9g==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "~0.61.0", + "@typespec/http": "~0.61.0" + } + }, + "node_modules/@typespec/sse": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.61.0.tgz", + "integrity": "sha512-q9wqMTqRDQkgID51o9lXWkrF9Ndn67sZznzGvKpCS6pG7eDc0cigkTWFmV2Agag9HzoP2MdFMlvU/sJKECPtfg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "~0.61.0", + "@typespec/events": "~0.61.0", + "@typespec/http": "~0.61.0", + "@typespec/streams": "~0.61.0" + } + }, + "node_modules/@typespec/streams": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.61.0.tgz", + "integrity": "sha512-MEFwYmYVibuTwVwJ6UKa9kgM3AP5bn/MWIhB/dTgYicXEgbUk+o9RHqg7JsySFyL0PO9XoqQBFiJhWM758f+pQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.60.0", - "@typespec/http": "~0.60.0" + "@typespec/compiler": "~0.61.0" } }, "node_modules/@typespec/versioning": { - "version": "0.60.1", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.60.1.tgz", - "integrity": "sha512-HogYL7P9uOPoSvkLLDjF22S6E9td6EY3c6TcIHhCzDTAQoi54csikD0gNrtcCkFG0UeQk29HgQymV397j+vp4g==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.61.0.tgz", + "integrity": "sha512-PIIug6eg3zc7E+BBHyNHHQD+OBq3FU465nhKrLEp35iVji/sYFuPc1ywnELDuwJVRWm6nvqNL1vtnc+4lEk+oA==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.60.0" + "@typespec/compiler": "~0.61.0" } }, "node_modules/ajv": { @@ -799,9 +847,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -1131,9 +1179,9 @@ } }, "node_modules/yaml": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", - "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/typespec/package.json b/typespec/package.json index d424d67dc..6d435a832 100644 --- a/typespec/package.json +++ b/typespec/package.json @@ -3,12 +3,14 @@ "version": "0.4.0", "type": "module", "dependencies": { - "@typespec/compiler": "^0.60.1", - "@typespec/http": "^0.60.0", - "@typespec/openapi": "^0.60.0", - "@typespec/openapi3": "^0.60.0", - "@typespec/rest": "^0.60.0", - "@typespec/versioning": "^0.60.1" + "@typespec/compiler": "^0.61.2", + "@typespec/events": "^0.61.0", + "@typespec/http": "^0.61.0", + "@typespec/openapi": "^0.61.0", + "@typespec/openapi3": "^0.61.0", + "@typespec/rest": "^0.61.0", + "@typespec/sse": "^0.61.0", + "@typespec/versioning": "^0.61.0" }, "private": true } diff --git a/typespec/sessions/models.tsp b/typespec/sessions/models.tsp index dfbb6ea41..bb1c16d19 100644 --- a/typespec/sessions/models.tsp +++ b/typespec/sessions/models.tsp @@ -62,14 +62,12 @@ model Session { /** Action to start on context window overflow */ context_overflow: ContextOverflowType | null = null; - /** Whether to forward the tool results to the model when available. - * "true" => always forward - * "false" => never forward - * null => forward if applicable (default) + /** Whether to auto-run the tool and send the tool results to the model when available. + * (default: false for sessions, true for tasks) * * If a tool call is made, the tool's output will be sent back to the model as the model's input. * If a tool call is not made, the model's output will be returned as is. */ - forward_tool_results: boolean | null = null; + auto_run_tools: boolean = false; ...HasId; ...HasMetadata; diff --git a/typespec/tasks/endpoints.tsp b/typespec/tasks/endpoints.tsp index 2d9ec422b..850eacdf3 100644 --- a/typespec/tasks/endpoints.tsp +++ b/typespec/tasks/endpoints.tsp @@ -15,6 +15,8 @@ namespace Tasks; // TASK ENDPOINTS // +interface GetEndpoints extends GetEndpoint {} + interface CreateOrUpdateEndpoints { @post @doc("Create or update a task") diff --git a/typespec/tasks/models.tsp b/typespec/tasks/models.tsp index b8b115861..c3b301bd2 100644 --- a/typespec/tasks/models.tsp +++ b/typespec/tasks/models.tsp @@ -33,7 +33,7 @@ model TaskTool extends CreateToolRequest { /** Reference to a tool by id */ model ToolRefById { @visibility("read", "create") - id?: uuid; + id?: string; } /** Reference to a tool by name */ @@ -65,8 +65,8 @@ model Task { /** Tools defined specifically for this task not included in the Agent itself. */ tools: TaskTool[] = #[]; - /** Whether to inherit tools from the parent agent or not. Defaults to true. */ - inherit_tools: boolean = true; + /** Whether to inherit tools from the parent agent or not. Defaults to false. */ + inherit_tools: boolean = false; ...HasId; ...HasTimestamps; diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 1d8c19209..a7c877401 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -29,18 +29,27 @@ alias TypedExpression = PyExpression; alias ReduceExpression> = TypedExpression; /** A string->string object where the values are python expressions that get evaluated to give a final object. */ -alias ExpressionObject = Record>; +alias ExpressionObject = Record | TypedExpression[] | Record> | Record>[]>; + +alias NestedExpression = Record | ExpressionObject | ExpressionObject[]>; /** Nested expression object. */ -alias NestedExpressionObject = Record | ExpressionObject>; +alias NestedExpressionObject = NestedExpression | NestedExpression[]; @discriminator("kind_") -model BaseWorkflowStep { +model BaseWorkflowStep { /** The kind of step */ @visibility("read") - kind_: T; + kind_: (typeof T); + + /** The label of this step for referencing it from other steps */ + label?: string; } +alias SequentialWorkflowStep = + | WaitForInputStep + | MappableWorkflowStep; + alias MappableWorkflowStep = | EvaluateStep | ToolCallStep @@ -55,7 +64,6 @@ alias NonConditionalWorkflowStep = | ReturnStep | SleepStep | ErrorWorkflowStep - | YieldStep | WaitForInputStep; alias ConditionalStep = IfElseWorkflowStep | SwitchStep; @@ -95,7 +103,7 @@ model PromptStepDef { prompt: JinjaTemplate | InputChatMLMessage[]; /** The tools to use for the prompt */ - tools: "all" | (ToolRef | CreateToolRequest)[] = #[]; + tools: "all" | (ToolRef | CreateToolRequest)[] = "all"; /** The tool choice for the prompt */ tool_choice?: ToolChoiceOption; @@ -106,14 +114,15 @@ model PromptStepDef { /** Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` */ unwrap?: boolean = false; - /** Whether to forward the tool results to the model when available. - * "true" => always forward - * "false" => never forward - * null => forward if applicable (default) - * + /** Whether to auto-run the tool and send the tool results to the model when available. + * (default: true for prompt steps, false for sessions) + * * If a tool call is made, the tool's output will be used as the model's input. * If a tool call is not made, the model's output will be used as the next step's input. */ - forward_tool_results: boolean | null = null; + auto_run_tools: boolean = true; + + /** Whether to disable caching for the prompt step */ + disable_cache: boolean = false; } model EvaluateStep extends BaseWorkflowStep<"evaluate"> { @@ -208,7 +217,7 @@ model ForeachDo { in: TypedExpression>; /** The steps to run for each iteration */ - do: MappableWorkflowStep; + do: SequentialWorkflowStep; } model ForeachStep extends BaseWorkflowStep<"foreach"> { diff --git a/typespec/tools/anthropic.tsp b/typespec/tools/anthropic.tsp new file mode 100644 index 000000000..2718b42e6 --- /dev/null +++ b/typespec/tools/anthropic.tsp @@ -0,0 +1,110 @@ +import "../common"; + +using Common; + +namespace Tools; + + +/** + * Anthropic new tools + */ + + +model Computer20241022Def { + type: "computer_20241022" = "computer_20241022"; + name?: string = "computer"; + + /** The display width in pixels */ + @minValue(600) + display_width_px: uint16 = 1024; + + /** The display height in pixels */ + @minValue(400) + display_height_px: uint16 = 768; + + /** The display number to use */ + @minValue(1) + @maxValue(10) + display_number?: uint16 = 1; +} + +model TextEditor20241022Def { + type: "text_editor_20241022" = "text_editor_20241022"; + name?: string = "str_replace_editor"; +} + +model Bash20241022Def { + type: "bash_20241022" = "bash_20241022"; + name?: string = "bash"; +} + +enum Computer20241022Action { + /** Press a key or key-combination on the keyboard */ + key, + + /** Type a string of text on the keyboard */ + type, + + /** Get the current (x, y) pixel coordinate of the cursor on the screen */ + cursor_position, + + /** Move the cursor to a specified (x, y) pixel coordinate on the screen */ + mouse_move, + + /** Click the left mouse button */ + left_click, + + /** Click the right mouse button */ + right_click, + + /** Click the middle mouse button */ + middle_click, + + /** Double-click the left mouse button */ + double_click, + + /** Take a screenshot of the screen */ + screenshot, +} + +model ChosenComputer20241022 { + /** The action to perform */ + action: Computer20241022Action; + + /** The text to type */ + text?: string; + + /** The (x, y) pixel coordinate to move the cursor to */ + coordinate?: uint16[]; +} + +model ChosenTextEditor20241022 { + /** The command to run */ + command: "str_replace" | "insert" | "view" | "undo_edit"; + + /** The path to the file */ + path: string; + + /** The content of the file to be created */ + file_text?: string; + + /** The line to insert the new string after */ + insert_line?: uint16; + + /** The new string to insert */ + new_str?: string; + + /** The string in the file to replace */ + old_str?: string; + + /** The line range to view */ + view_range?: uint16[]; +} + +model ChosenBash20241022 { + /** The bash command to run */ + command?: string; + + /** Whether to restart the tool */ + restart?: boolean = false; +} diff --git a/typespec/tools/arxiv.tsp b/typespec/tools/arxiv.tsp new file mode 100644 index 000000000..09ed8a0f1 --- /dev/null +++ b/typespec/tools/arxiv.tsp @@ -0,0 +1,65 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Arguments for Arxiv Search */ +model ArxivSearchArguments { + /** The search query for searching with Arxiv */ + query: string; + + /** The list of Arxiv IDs to search with */ + id_list?: Array; + + /** The maximum number of results to return */ + @minValue(1) + @maxValue(300000) + max_results?: int16 = 5; + + /** The download the pdf of the results */ + download_pdf?: boolean = false; + + /** The sort criterion for the results */ + sort_by?: "relevance" | "lastUpdatedDate" | "submittedDate" = "relevance"; + + /** The sort order for the results */ + sort_order?: "ascending" | "descending" = "descending"; +} + +/** Arxiv integration definition */ +model ArxivIntegrationDef extends BaseIntegrationDef { + /** The provider must be "arxiv" */ + provider: "arxiv" = "arxiv"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Arxiv */ + setup?: null = null; + + /** The arguments for Arxiv Search */ + arguments?: ArxivSearchArguments; +} + +/** The result of the Arxiv Search */ +model ArxivSearchOutput { + result: string; +} + +model ArxivProviderCard extends BaseProviderCard { + provider: "arxiv" = "arxiv"; + setup: null = null; + methods: ProviderMethod[] = #[ + #{ + method: "search", + description: "Search with Arxiv", + } + ]; + info: ProviderInfo = #{ + url: "https://pypi.org/project/arxiv/", + docs: "https://info.arxiv.org/help/api/index.html", + icon: "https://arxiv.com/favicon.ico", + friendly_name: "Arxiv Search", + }; +} diff --git a/typespec/tools/brave.tsp b/typespec/tools/brave.tsp new file mode 100644 index 000000000..f81ac12f1 --- /dev/null +++ b/typespec/tools/brave.tsp @@ -0,0 +1,54 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Integration definition for Brave Search */ +model BraveSearchSetup { + /** The api key for Brave Search */ + api_key: string; +} + +/** Arguments for Brave Search */ +model BraveSearchArguments { + /** The search query for searching with Brave */ + query: string; +} + +/** Brave integration definition */ +model BraveIntegrationDef extends BaseIntegrationDef { + /** The provider must be "brave" */ + provider: "brave" = "brave"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Brave */ + setup?: BraveSearchSetup; + + /** The arguments for Brave Search */ + arguments?: BraveSearchArguments; +} + +/** The result of the Brave Search */ +model BraveSearchOutput { + result: string; +} + +model BraveProviderCard extends BaseProviderCard { + provider: "brave" = "brave"; + setup: BraveSearchSetup; + methods: ProviderMethod[] = #[ + #{ + method: "search", + description: "Search with Brave", + } + ]; + info: ProviderInfo = #{ + url: "https://brave.com/", + docs: "https://brave.com/docs/", + icon: "https://brave.com/favicon.ico", + friendly_name: "Brave Search", + }; +} \ No newline at end of file diff --git a/typespec/tools/browserbase/contexts.tsp b/typespec/tools/browserbase/contexts.tsp new file mode 100644 index 000000000..065487728 --- /dev/null +++ b/typespec/tools/browserbase/contexts.tsp @@ -0,0 +1,29 @@ +import "../../common"; + +using Common; + +namespace Tools; + +// TODO: Implement these +/** browserbase context provider */ +model BrowserbaseContextIntegrationDef extends BaseBrowserbaseIntegrationDef { + /** The specific method of the integration to call */ + method?: "create_context" = "create_context"; + + /** The arguments for the method */ + arguments?: BrowserbaseContextArguments; +} + +model BrowserbaseContextArguments { + /** The Project ID. Can be found in Settings. */ + projectId: string; +} + +model BrowserbaseContextOutput { + id: string; + uploadUrl?: url; + publicKey?: string; + cipherAlgorithm?: string; + initializationVectorSize?: uint16; +} + diff --git a/typespec/tools/browserbase/extensions.tsp b/typespec/tools/browserbase/extensions.tsp new file mode 100644 index 000000000..6cdba57ed --- /dev/null +++ b/typespec/tools/browserbase/extensions.tsp @@ -0,0 +1,28 @@ +import "../../common"; + +using Common; + +namespace Tools; + + +/** browserbase extension provider */ +model BrowserbaseExtensionIntegrationDef extends BaseBrowserbaseIntegrationDef { + /** The specific method of the integration to call */ + method?: "install_extension_from_github"; + + /** The arguments for the method */ + arguments?: BrowserbaseExtensionArguments; +} + +model BrowserbaseExtensionArguments { + /** The GitHub repository name. */ + repositoryName: string; + + /** Ref to install from a branch or tag. */ + ref?: string; +} + +model BrowserbaseExtensionOutput { + /** The installed Extension ID. */ + id: string; +} diff --git a/typespec/tools/browserbase/main.tsp b/typespec/tools/browserbase/main.tsp new file mode 100644 index 000000000..7f932ce79 --- /dev/null +++ b/typespec/tools/browserbase/main.tsp @@ -0,0 +1,94 @@ +import "../../common"; +import "./contexts.tsp"; +import "./extensions.tsp"; +import "./sessions.tsp"; + +namespace Tools; + +using Common; + +alias BrowserbaseMethod = + | /** Get live URLs from the browserbase context */ + "get_live_urls" + | /** List the sessions in the browserbase context */ + "list_sessions" + | /** Create a new session in the browserbase context */ + "create_session" + | /** Get a session from the browserbase context */ + "get_session" + | /** Complete a session in the browserbase context */ + "complete_session" + | /** Get the connection URL for a session */ + "get_connect_url" + | /** Install an extension from GitHub to the browserbase context */ + "install_extension_from_github" + | // TODO: Implement these + /** Create a new browserbase context */ + "create_context" + | /** Get session downloads from the browserbase context */ + "get_session_downloads" + | /** Get logs from the browserbase context */ + "get_logs" + | /** Get recordings from the browserbase context */ + "get_recordings"; + +/** The setup parameters for the browserbase integration */ +model BrowserbaseSetup { + /** API key for the browserbase integration */ + api_key: string; + /** The project ID. Can be found in Settings. */ + project_id: string; + /** The API URL. Defaults to https://www.browserbase.com */ + api_url?: string; + /** The connect URL. Defaults to wss://connect.browserbase.com */ + connect_url?: string; +} + +/** The base definition for a browserbase integration */ +model BaseBrowserbaseIntegrationDef extends BaseIntegrationDef { + provider: "browserbase" = "browserbase"; + setup?: BrowserbaseSetup; + method?: BrowserbaseMethod; + arguments?: unknown; +} + +alias BrowserbaseIntegrationDef = BrowserbaseContextIntegrationDef | BrowserbaseExtensionIntegrationDef | BrowserbaseSessionIntegrationDef; + +alias BrowserbaseArguments = BrowserbaseSessionArguments | BrowserbaseContextArguments | BrowserbaseExtensionArguments; + +alias BrowserbaseOutput = BrowserbaseSessionOutput | BrowserbaseContextOutput | BrowserbaseExtensionOutput; + +model BrowserbaseProviderCard extends BaseProviderCard { + provider: "browserbase" = "browserbase"; + setup: BrowserbaseSetup; + methods: ProviderMethod[] = #[ + #{ + method: "list_sessions", + description: "List the sessions in the browserbase context", + }, + #{ + method: "create_session", + description: "Create a new session in the browserbase context", + }, + #{ + method: "get_session", + description: "Get a session from the browserbase context", + }, + #{ + method: "complete_session", + description: "Complete a session in the browserbase context", + }, + #{ + method: "get_live_urls", + description: "Get live URLs from the browserbase context", + }, + #{ + method: "install_extension_from_github", + description: "Install an extension from GitHub to the browserbase context", + }, + #{ + method: "get_connect_url", + description: "Get the connection URL for a session", + } + ]; +} diff --git a/typespec/tools/browserbase/sessions.tsp b/typespec/tools/browserbase/sessions.tsp new file mode 100644 index 000000000..cfbbc6d56 --- /dev/null +++ b/typespec/tools/browserbase/sessions.tsp @@ -0,0 +1,221 @@ +import "../../common"; + +using Common; + +namespace Tools; + +alias BrowserbaseSessionMethod = + | /** Get live URLs from the browserbase context */ + "get_live_urls" + | /** List the sessions in the browserbase context */ + "list_sessions" + | /** Create a new session in the browserbase context */ + "create_session" + | /** Get a session from the browserbase context */ + "get_session" + | /** Complete a session in the browserbase context */ + "complete_session" + | /** Get the connection URL for a session */ + "get_connect_url" + | // TODO: Implement these + "get_session_downloads" + | "get_logs" + | "get_recordings"; + +alias BrowserbaseSessionIntegrationDef = + | BrowserbaseListSessionsIntegrationDef + | BrowserbaseCreateSessionIntegrationDef + | BrowserbaseGetSessionIntegrationDef + | BrowserbaseCompleteSessionIntegrationDef + | BrowserbaseGetSessionLiveUrlsIntegrationDef + | BrowserbaseGetSessionConnectUrlIntegrationDef; +// +// TODO: Implement these +// | BrowserbaseCreateSessionUploadsIntegrationDef +// | BrowserbaseGetSessionDownloadsIntegrationDef +// | BrowserbaseGetLogsIntegrationDef +// | BrowserbaseGetRecordingsIntegrationDef + +alias BrowserbaseSessionArguments = + | BrowserbaseListSessionsArguments + | BrowserbaseCreateSessionArguments + | BrowserbaseGetSessionArguments + | BrowserbaseCompleteSessionArguments + | BrowserbaseGetSessionLiveUrlsArguments + | BrowserbaseGetSessionConnectUrlArguments; +// +// TODO: Implement these +// | BrowserbaseCreateSessionUploadsArguments +// | BrowserbaseGetSessionDownloadsArguments +// | BrowserbaseGetLogsArguments +// | BrowserbaseGetRecordingsArguments + +alias BrowserbaseSessionOutput = + | BrowserbaseListSessionsOutput + | BrowserbaseCreateSessionOutput + | BrowserbaseGetSessionOutput + | BrowserbaseCompleteSessionOutput + | BrowserbaseGetSessionLiveUrlsOutput + | BrowserbaseGetSessionConnectUrlOutput; +// +// TODO: Implement these +// | BrowserbaseGetSessionDownloadsOutput +// | BrowserbaseGetLogsOutput +// | BrowserbaseGetRecordingsOutput + +/** browserbase list sessions integration definition */ +model BrowserbaseListSessionsIntegrationDef + extends BaseBrowserbaseIntegrationDef { + /** The specific method of the integration to call */ + method: "list_sessions" = "list_sessions"; + + /** The arguments for the method */ + arguments?: BrowserbaseListSessionsArguments; +} + +model BrowserbaseListSessionsArguments { + /** The status of the sessions to list (Available options: RUNNING, ERROR, TIMED_OUT, COMPLETED) */ + status?: "RUNNING" | "ERROR" | "TIMED_OUT" | "COMPLETED"; +} + +alias BrowserbaseListSessionsOutput = { + id: string; + createdAt?: string; + updatedAt?: string; + projectId?: string; + startedAt?: string; + endedAt?: string; + expiresAt?: string; + status?: "RUNNING" | "ERROR" | "TIMED_OUT" | "COMPLETED"; + proxyBytes?: uint64; + avgCpuUsage?: uint64; + memoryUsage?: uint64; + keepAlive?: boolean; + contextId?: string; +}[]; + +/** browserbase create session integration definition */ +model BrowserbaseCreateSessionIntegrationDef + extends BaseBrowserbaseIntegrationDef { + method: "create_session" = "create_session"; + + /** The arguments for the method */ + arguments?: BrowserbaseCreateSessionArguments; +} + +model BrowserbaseCreateSessionArguments { + /** The Project ID. Can be found in Settings. */ + projectId?: string; + + /** The installed Extension ID. See Install Extension from GitHub. */ + extensionId?: string; + + /** Browser settings */ + browserSettings?: Record = #{}; + + /** Duration in seconds after which the session will automatically end. Defaults to the Project's defaultTimeout. */ + timeout?: uint16 = 3600; + + /** Set to true to keep the session alive even after disconnections. This is available on the Startup plan only. */ + keepAlive?: boolean = false; + + /** Proxy configuration. Can be true for default proxy, or an array of proxy configurations. */ + proxies?: boolean | Record[] = false; +} + +model BrowserbaseCreateSessionOutput { + id: string; + createdAt?: string; + projectId?: string; + startedAt?: string; + endedAt?: string; + expiresAt?: string; + status?: "RUNNING" | "ERROR" | "TIMED_OUT" | "COMPLETED"; + proxyBytes?: uint64; + avgCpuUsage?: uint64; + memoryUsage?: uint64; + keepAlive?: boolean; + contextId?: string; +} + +/** browserbase get session integration definition */ +model BrowserbaseGetSessionIntegrationDef + extends BaseBrowserbaseIntegrationDef { + method: "get_session" = "get_session"; + arguments?: BrowserbaseGetSessionArguments; +} + +model BrowserbaseGetSessionArguments { + id: string; +} + +model BrowserbaseGetSessionOutput { + id: string; + createdAt?: string; + projectId?: string; + startedAt?: string; + endedAt?: string; + expiresAt?: string; + status?: "RUNNING" | "ERROR" | "TIMED_OUT" | "COMPLETED"; + proxyBytes?: uint64; + avgCpuUsage?: uint64; + memoryUsage?: uint64; + keepAlive?: boolean; + contextId?: string; +} + +/** browserbase complete session integration definition */ +model BrowserbaseCompleteSessionIntegrationDef + extends BaseBrowserbaseIntegrationDef { + method: "complete_session" = "complete_session"; + arguments?: BrowserbaseCompleteSessionArguments; +} + +model BrowserbaseCompleteSessionArguments { + id: string; + status?: "REQUEST_RELEASE" = "REQUEST_RELEASE"; +} + +model BrowserbaseCompleteSessionOutput { + success: boolean; +} + +/** browserbase get session live urls integration definition */ +model BrowserbaseGetSessionLiveUrlsIntegrationDef + extends BaseBrowserbaseIntegrationDef { + method: "get_live_urls" = "get_live_urls"; + arguments?: BrowserbaseGetSessionLiveUrlsArguments; +} + +model BrowserbaseGetSessionLiveUrlsArguments { + id: string; +} + +model BrowserbaseGetSessionLiveUrlsOutput { + debuggerFullscreenUrl?: url; + debuggerUrl?: url; + wsUrl?: url; + pages: { + id?: string; + url?: url; + faviconUrl?: url; + title?: string; + debuggerUrl?: url; + debuggerFullscreenUrl?: url; + }[] = #[]; +} + +/** browserbase get session connect url integration definition */ +model BrowserbaseGetSessionConnectUrlIntegrationDef + extends BaseBrowserbaseIntegrationDef { + method: "get_connect_url" = "get_connect_url"; + arguments?: BrowserbaseGetSessionConnectUrlArguments; +} + +model BrowserbaseGetSessionConnectUrlArguments { + id: string; +} + +model BrowserbaseGetSessionConnectUrlOutput { + url: url; +} diff --git a/typespec/tools/cloudinary.tsp b/typespec/tools/cloudinary.tsp new file mode 100644 index 000000000..ec66962e1 --- /dev/null +++ b/typespec/tools/cloudinary.tsp @@ -0,0 +1,122 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Setup parameters for Cloudinary integration */ +model CloudinarySetup { + /** The API key for Cloudinary */ + cloudinary_api_key: string; + + /** The API secret for Cloudinary */ + cloudinary_api_secret: string; + + /** The Cloud name for Cloudinary */ + cloudinary_cloud_name: string; + + /** Additional parameters for the Cloudinary API */ + params?: Record; +} + +alias CloudinaryMethod = + | /** Upload media to Cloudinary */ + "media_upload" + | /** Edit media in Cloudinary */ + "media_edit"; + +/** Arguments for Cloudinary media upload */ +model CloudinaryUploadArguments { + /** The URL of the file upload */ + file: string; + + /** Return base64 encoded file */ + return_base64: boolean = false; + + /** Optional public ID for the uploaded file */ + public_id?: string; + + /** Optional upload parameters */ + upload_params?: Record; +} + +/** Arguments for Cloudinary media edit */ +model CloudinaryEditArguments { + /** The file Public ID in Cloudinary */ + public_id: string; + + /** The transformation to apply to the file */ + transformation: Array>; + + /** Return base64 encoded file */ + return_base64: boolean = false; +} + +/** Base Cloudinary integration definition */ +model BaseCloudinaryIntegrationDef extends BaseIntegrationDef { + provider: "cloudinary" = "cloudinary"; + setup?: CloudinarySetup; + method?: CloudinaryMethod; +} + +/** Cloudinary upload integration definition */ +model CloudinaryUploadIntegrationDef extends BaseCloudinaryIntegrationDef { + method: "media_upload" = "media_upload"; + arguments?: CloudinaryUploadArguments; +} + +/** Cloudinary edit integration definition */ +model CloudinaryEditIntegrationDef extends BaseCloudinaryIntegrationDef { + method: "media_edit" = "media_edit"; + arguments?: CloudinaryEditArguments; +} + +alias CloudinaryIntegrationDef = CloudinaryUploadIntegrationDef | CloudinaryEditIntegrationDef; + +/** Output for Cloudinary upload */ +model CloudinaryUploadOutput { + /** The URL of the uploaded file */ + url: string; + + /** The public ID of the uploaded file */ + public_id: string; + + /** The base64 encoded file */ + base64?: string; + + /** The metadata of the uploaded file */ + metadata: Record; +} + +/** Output for Cloudinary edit */ +model CloudinaryEditOutput { + /** The transformed URL from Cloudinary */ + transformed_url: string; + + /** The base64 encoded file */ + base64?: string; +} + +alias CloudinaryOutput = CloudinaryUploadOutput | CloudinaryEditOutput; + +/** Cloudinary Provider Card */ +model CloudinaryProviderCard extends BaseProviderCard { + provider: "cloudinary" = "cloudinary"; + setup: CloudinarySetup; + methods: ProviderMethod[] = #[ + #{ + method: "media_upload", + description: "Upload media to Cloudinary", + }, + #{ + method: "media_edit", + description: "Edit media in Cloudinary", + } + ]; + info: ProviderInfo = #{ + url: "https://cloudinary.com/", + docs: "https://cloudinary.com/documentation/python_quickstart", + icon: "https://cloudinary.com/favicon.ico", + friendly_name: "Cloudinary", + }; +} \ No newline at end of file diff --git a/typespec/tools/email.tsp b/typespec/tools/email.tsp new file mode 100644 index 000000000..e0864fc7e --- /dev/null +++ b/typespec/tools/email.tsp @@ -0,0 +1,74 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Setup parameters for Email integration */ +model EmailSetup { + /** The host of the email server */ + host: string; + + /** The port of the email server */ + port: int32; + + /** The username of the email server */ + user: string; + + /** The password of the email server */ + password: string; +} + +/** Arguments for Email sending */ +model EmailArguments { + /** The email address to send the email to */ + to: string; + + /** The email address to send the email from */ + from: string; + + /** The subject of the email */ + subject: string; + + /** The body of the email */ + body: string; +} + +/** Email integration definition */ +model EmailIntegrationDef extends BaseIntegrationDef { + /** The provider must be "email" */ + provider: "email" = "email"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Email */ + setup?: EmailSetup; + + /** The arguments for Email sending */ + arguments?: EmailArguments; +} + +/** Email Provider Card */ +model EmailProviderCard extends BaseProviderCard { + provider: "email" = "email"; + setup: EmailSetup; + methods: ProviderMethod[] = #[ + #{ + method: "send", + description: "Send an email", + } + ]; + info: ProviderInfo = #{ + url: "https://emailservice.com/", + docs: "https://emailservice.com/docs/", + icon: "https://emailservice.com/favicon.ico", + friendly_name: "Email Service", + }; +} + +/** Email Output */ +model EmailOutput { + /** Whether the email was sent successfully */ + success: boolean; +} \ No newline at end of file diff --git a/typespec/tools/ffmpeg.tsp b/typespec/tools/ffmpeg.tsp new file mode 100644 index 000000000..a8f8baf03 --- /dev/null +++ b/typespec/tools/ffmpeg.tsp @@ -0,0 +1,56 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Arguments for Ffmpeg CMD */ +model FfmpegSearchArguments { + /** The bash command string */ + cmd: string; + + /** The base64 string of the file*/ + file?: string; + +} + +/** Ffmpeg integration definition */ +model FfmpegIntegrationDef extends BaseIntegrationDef { + /** The provider must be "ffmpeg" */ + provider: "ffmpeg" = "ffmpeg"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Ffmpeg */ + setup?: null = null; + + /** The arguments for Ffmpeg Search */ + arguments?: FfmpegSearchArguments; +} + +/** Ffmpeg Provider Card */ +model FfmpegProviderCard extends BaseProviderCard { + provider: "ffmpeg" = "ffmpeg"; + setup: null = null; + methods: ProviderMethod[] = #[ + #{ + method: "bash_cmd", + description: "Run FFmpeg bash command", + } + ]; + info: ProviderInfo = #{ + url: "https://ffmpeg.org/", + docs: "https://ffmpeg.org/documentation.html", + icon: "https://upload.wikimedia.org/wikipedia/commons/5/5f/FFmpeg_Logo_new.svg", + friendly_name: "Ffmpeg", + }; +} + +/** Ffmpeg Search Output */ +model FfmpegSearchOutput { + /** The documents returned from the Ffmpeg search */ + file: string; + result: boolean; + mime_type: string; +} \ No newline at end of file diff --git a/typespec/tools/llama_parse.tsp b/typespec/tools/llama_parse.tsp new file mode 100644 index 000000000..cdafed0ac --- /dev/null +++ b/typespec/tools/llama_parse.tsp @@ -0,0 +1,75 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Setup parameters for LlamaParse integration */ +model LlamaParseSetup { + /** The API key for LlamaParse */ + llamaparse_api_key: string; + + /** Optional upload parameters */ + params?: Record; +} + +/** Arguments for LlamaParse integration */ +model LlamaParseFetchArguments { + /** File Name. If not provided, a random name will be generated. */ + filename?: string; + + /** The base64 string of the file, which can be a single string or a list of strings */ + file: string | Array; + + /** Optional upload parameters */ + params?: Record; + + /** The input file is base64 */ + base64?: boolean = false; +} + +/** LlamaParse integration definition */ +model LlamaParseIntegrationDef extends BaseIntegrationDef { + /** The provider must be "LlamaParseSetup" */ + provider: "llama_parse" = "llama_parse"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for LlamaParse */ + setup?: LlamaParseSetup; + + /** The arguments for LlamaParse */ + arguments?: LlamaParseFetchArguments; +} + +/** LlamaParse Provider Card */ +model LlamaParseProviderCard extends BaseProviderCard { + provider: "llama_parse" = "llama_parse"; + setup: LlamaParseSetup; + methods: ProviderMethod[] = #[ + #{ + method: "parse", + description: "Parse and Extract the Files", + } + ]; + info: ProviderInfo = #{ + url: "https://www.llamaindex.ai/", + docs: "https://docs.cloud.llamaindex.ai/llamaparse/getting_started", + icon: "https://www.llamaindex.ai/favicon.ico", + friendly_name: "LlamaParse", + }; +} + +/** Represents a document with text content */ +model LlamaParseDocument { + // Using string for now since we need to represent langchain Document + text: string; + metadata: Record; +} + +/** LlamaParse Fetch Output */ +model LlamaParseFetchOutput { + /** The documents returned from the LlamaParse */ + documents: LlamaParseDocument[]; +} \ No newline at end of file diff --git a/typespec/tools/main.tsp b/typespec/tools/main.tsp index 56b098047..d7a2d4eeb 100644 --- a/typespec/tools/main.tsp +++ b/typespec/tools/main.tsp @@ -1,5 +1,17 @@ import "./endpoints.tsp"; import "./models.tsp"; +import "./anthropic.tsp"; +import "./brave.tsp"; +import "./email.tsp"; +import "./spider.tsp"; +import "./weather.tsp"; +import "./wikipedia.tsp"; +import "./browserbase"; +import "./remote_browser.tsp"; +import "./llama_parse.tsp"; +import "./ffmpeg.tsp"; +import "./cloudinary.tsp"; +import "./arxiv.tsp"; namespace Tools; diff --git a/typespec/tools/models.tsp b/typespec/tools/models.tsp index de509dec4..2eeb72306 100644 --- a/typespec/tools/models.tsp +++ b/typespec/tools/models.tsp @@ -16,13 +16,17 @@ alias httpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIO /** Integration provider name */ alias integrationProvider = ( | "dummy" - | "hacker_news" | "weather" | "wikipedia" | "spider" | "brave" | "browserbase" | "email" + | "remote_browser" + | "llama_parse" + | "ffmpeg" + | "cloudinary" + | "arxiv" ); enum ToolType { @@ -37,6 +41,11 @@ enum ToolType { /** A tool that makes an API call */ api_call, + + /** (Alpha) Anthropic new tools */ + computer_20241022, + text_editor_20241022, + bash_20241022, } /** The parameters the functions accepts, described as a JSON Schema object. */ @@ -59,23 +68,62 @@ model FunctionDef { } -// TODO: Add granular definitions for each integration - /** Integration definition */ -model IntegrationDef { +@discriminator("provider") +model BaseIntegrationDef { /** The provider of the integration */ - provider: integrationProvider | string; + provider: integrationProvider; /** The specific method of the integration to call */ method?: string; /** The setup parameters the integration accepts */ - setup?: FunctionParameters; + setup?: unknown; /** The arguments to pre-apply to the integration call */ - arguments?: FunctionParameters; + arguments?: unknown; +} + +model DummyIntegrationDef extends BaseIntegrationDef { + provider: "dummy" = "dummy"; +} + +model BaseProviderCard { + provider: integrationProvider; + setup: unknown; + methods: ProviderMethod[]; + info: ProviderInfo; } +model ProviderMethod { + method: string; + description?: string; + arguments?: Arguments; + output?: Output; +} + +model ProviderInfo { + url?: url; + docs?: url; + icon?: url; + friendly_name?: string; +} + +alias IntegrationDef = ( + | DummyIntegrationDef + | BraveIntegrationDef + | EmailIntegrationDef + | SpiderIntegrationDef + | WikipediaIntegrationDef + | WeatherIntegrationDef + | BrowserbaseIntegrationDef + | RemoteBrowserIntegrationDef + | LlamaParseIntegrationDef + | FfmpegIntegrationDef + | CloudinaryIntegrationDef + | ArxivIntegrationDef +); + // // SYSTEM TOOL MODELS // @@ -140,6 +188,9 @@ model ApiCallDef { /** The URL to call */ url: url; + /** The schema of the response */ + schema?: Record; + /** The headers to send with the request */ headers?: Record; @@ -149,6 +200,9 @@ model ApiCallDef { /** The data to send as form data */ data?: Record; + /** The data to send as files data */ + files?: Record; + /** JSON body to send with the request */ json?: Record; @@ -170,6 +224,9 @@ model Tool { /** Name of the tool (must be unique for this agent and a valid python identifier string )*/ name: validPythonIdentifier; + /** Type of the tool */ + type: ToolType; + /** Description of the tool */ description?: string; @@ -185,6 +242,11 @@ model Tool { /** The API call to make */ api_call?: ApiCallDef; + /** (Alpha) Anthropic new tools */ + computer_20241022?: Computer20241022Def; + text_editor_20241022?: TextEditor20241022Def; + bash_20241022?: Bash20241022Def; + ...HasTimestamps; ...HasId; } @@ -192,6 +254,9 @@ model Tool { model FunctionCallOption { /** The name of the function */ name: string; + + /** The parameters to pass to the function */ + arguments?: string; // May be a JSON string } model NamedToolChoice { @@ -202,7 +267,7 @@ model NamedToolChoice { } model ToolResponse { - @key id: uuid; + @key id: string; /** The output of the tool */ output: ToolOutput; @@ -224,22 +289,37 @@ model UpdateToolRequest { model PatchToolRequest is UpdateToolRequest {} /** The response tool value generated by the model */ -@discriminator("type") -model ChosenToolCall { +model BaseChosenToolCall { /** Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) */ type: ToolType; function?: FunctionCallOption; - integration?: never; - system?: never; - api_call?: never; + integration?: unknown; // ChosenIntegrationCall + system?: unknown; // ChosenSystemCall + api_call?: unknown; // ChosenApiCall - ...HasId; + /** (Alpha) Anthropic new tools */ + computer_20241022?: ChosenComputer20241022; + text_editor_20241022?: ChosenTextEditor20241022; + bash_20241022?: ChosenBash20241022; + + @visibility("read") + id?: string; } -model ChosenFunctionCall extends ChosenToolCall { +model ChosenFunctionCall extends BaseChosenToolCall { type: ToolType.function; /** The function to call */ function: FunctionCallOption; } + +alias ChosenToolCall = ( + | ChosenFunctionCall + // | ChosenIntegrationCall + // | ChosenSystemCall + // | ChosenApiCall + | ChosenComputer20241022 + | ChosenTextEditor20241022 + | ChosenBash20241022 +); diff --git a/typespec/tools/remote_browser.tsp b/typespec/tools/remote_browser.tsp new file mode 100644 index 000000000..88df6697d --- /dev/null +++ b/typespec/tools/remote_browser.tsp @@ -0,0 +1,98 @@ +import "../common"; + +using Common; + +namespace Tools; + +alias RemoteBrowserAction = + | /** Press a key */ + "key" + | /** Type text */ + "type" + | /** Move the mouse to a coordinate */ + "mouse_move" + | /** Left click */ + "left_click" + | /** Left click and drag */ + "left_click_drag" + | /** Right click */ + "right_click" + | /** Middle click */ + "middle_click" + | /** Double click */ + "double_click" + | /** Take a screenshot */ + "screenshot" + | /** Get the cursor position */ + "cursor_position" + | /** Navigate to a URL */ + "navigate" + | /** Refresh the page */ + "refresh"; + +/** The setup parameters for the remote browser */ +model RemoteBrowserSetup { + /** The connection URL for the remote browser */ + connect_url?: string; + + /** The width of the browser */ + width?: uint16; + + /** The height of the browser */ + height?: uint16; +} + +/** The arguments for the remote browser */ +model RemoteBrowserArguments { + /** The connection URL for the remote browser */ + connect_url?: string; + + /** The action to perform */ + action: RemoteBrowserAction; + + /** The text */ + text?: string; + + /** The coordinate to move the mouse to */ + coordinate?: [uint16, uint16]; +} + +/** The output of the remote browser */ +model RemoteBrowserOutput { + /** The output of the action */ + output: string | null = null; + + /** The error of the action */ + error: string | null = null; + + /** The base64 encoded image of the action */ + base64_image: string | null = null; + + /** The system output of the action */ + system: string | null = null; +} + +/** The integration definition for the remote browser */ +model RemoteBrowserIntegrationDef extends BaseIntegrationDef { + provider: "remote_browser" = "remote_browser"; + setup: RemoteBrowserSetup; + method: "perform_action" = "perform_action"; + arguments?: RemoteBrowserArguments; +} + +model RemoteBrowserProviderCard extends BaseProviderCard { + provider: "remote_browser" = "remote_browser"; + setup: RemoteBrowserSetup; + methods: ProviderMethod[] = #[ + #{ + method: "perform_action", + description: "Perform an action in the remote browser", + } + ]; + info: ProviderInfo = #{ + url: "https://playwright.dev/", + docs: "https://playwright.dev/docs/api/class-page", + icon: "https://playwright.dev/favicon.ico", + friendly_name: "Playwright", + }; +} diff --git a/typespec/tools/spider.tsp b/typespec/tools/spider.tsp new file mode 100644 index 000000000..9b7176f4c --- /dev/null +++ b/typespec/tools/spider.tsp @@ -0,0 +1,69 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Setup parameters for Spider integration */ +model SpiderSetup { + /** The API key for Spider */ + spider_api_key: string; +} + +/** Arguments for Spider integration */ +model SpiderFetchArguments { + /** The URL to fetch data from */ + url: url; + + /** The type of crawler to use */ + mode?: "scrape" = "scrape"; + + /** Additional parameters for the Spider API */ + params?: Record; +} + +/** Spider integration definition */ +model SpiderIntegrationDef extends BaseIntegrationDef { + /** The provider must be "spider" */ + provider: "spider" = "spider"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Spider */ + setup?: SpiderSetup; + + /** The arguments for Spider */ + arguments?: SpiderFetchArguments; +} + +/** Spider Provider Card */ +model SpiderProviderCard extends BaseProviderCard { + provider: "spider" = "spider"; + setup: SpiderSetup; + methods: ProviderMethod[] = #[ + #{ + method: "crawl", + description: "Crawl a website and extract data", + } + ]; + info: ProviderInfo = #{ + url: "https://spider.com/", + docs: "https://spider.com/docs/", + icon: "https://spider.com/favicon.ico", + friendly_name: "Spider", + }; +} + +/** Spider Fetch Output */ +model SpiderFetchOutput { + /** The documents returned from the spider */ + documents: SpiderDocument[]; +} + +/** Represents a document with text content */ +model SpiderDocument { + // Using string for now since we need to represent langchain Document + page_content: string; + metadata: Record; +} \ No newline at end of file diff --git a/typespec/tools/weather.tsp b/typespec/tools/weather.tsp new file mode 100644 index 000000000..a4047c064 --- /dev/null +++ b/typespec/tools/weather.tsp @@ -0,0 +1,56 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Integration definition for Weather */ +model WeatherSetup { + /** The api key for OpenWeatherMap */ + openweathermap_api_key: string; +} + +/** Arguments for Weather */ +model WeatherGetArguments { + /** The location for which to fetch weather data */ + location: string; +} + +/** Weather integration definition */ +model WeatherIntegrationDef extends BaseIntegrationDef { + /** The provider must be "weather" */ + provider: "weather" = "weather"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Weather */ + setup?: WeatherSetup; + + /** The arguments for Weather */ + arguments?: WeatherGetArguments; +} + +/** Weather Provider Card */ +model WeatherProviderCard extends BaseProviderCard { + provider: "weather" = "weather"; + setup: WeatherSetup; + methods: ProviderMethod[] = #[ + #{ + method: "get", + description: "Get the current weather for a city", + } + ]; + info: ProviderInfo = #{ + url: "https://www.weatherapi.com/", + docs: "https://www.weatherapi.com/docs/", + icon: "https://www.weatherapi.com/favicon.ico", + friendly_name: "Weather API", + }; +} + +/** Weather Get Output */ +model WeatherGetOutput { + /** The weather data for the specified location */ + result: string; +} \ No newline at end of file diff --git a/typespec/tools/wikipedia.tsp b/typespec/tools/wikipedia.tsp new file mode 100644 index 000000000..143bb519d --- /dev/null +++ b/typespec/tools/wikipedia.tsp @@ -0,0 +1,62 @@ +import "../common"; + +using Common; + +namespace Tools; + +/** Arguments for Wikipedia Search */ +model WikipediaSearchArguments { + /** The search query string */ + query: string; + + /** Maximum number of documents to load */ + @minValue(1) + @maxValue(10) + load_max_docs: uint8 = 2; +} + +/** Wikipedia integration definition */ +model WikipediaIntegrationDef extends BaseIntegrationDef { + /** The provider must be "wikipedia" */ + provider: "wikipedia" = "wikipedia"; + + /** The specific method of the integration to call */ + method?: string; + + /** The setup parameters for Wikipedia */ + setup?: null = null; + + /** The arguments for Wikipedia Search */ + arguments?: WikipediaSearchArguments; +} + +/** Wikipedia Provider Card */ +model WikipediaProviderCard extends BaseProviderCard { + provider: "wikipedia" = "wikipedia"; + setup: null = null; + methods: ProviderMethod[] = #[ + #{ + method: "search", + description: "Search for a page on Wikipedia", + } + ]; + info: ProviderInfo = #{ + url: "https://www.wikipedia.org/", + docs: "https://www.wikipedia.org/wiki/Main_Page", + icon: "https://www.wikipedia.org/static/favicon/wikipedia.ico", + friendly_name: "Wikipedia", + }; +} + +/** Represents a document with text content */ +model WikipediaDocument { + // Using string for now since we need to represent langchain Document + page_content: string; + metadata: Record; +} + +/** Wikipedia Search Output */ +model WikipediaSearchOutput { + /** The documents returned from the Wikipedia search */ + documents: WikipediaDocument[]; +} \ No newline at end of file diff --git a/typespec/tsp-output/@typespec/openapi3/openapi-0.4.0.yaml b/typespec/tsp-output/@typespec/openapi3/openapi-0.4.0.yaml deleted file mode 100644 index d00a5b9d2..000000000 --- a/typespec/tsp-output/@typespec/openapi3/openapi-0.4.0.yaml +++ /dev/null @@ -1,6238 +0,0 @@ -openapi: 3.0.0 -info: - title: Julep API - termsOfService: https://julep.ai/terms - contact: - name: Julep AI - url: https://julep.ai - email: developers@julep.ai - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html - summary: A backend for creating stateful AI apps - description: Julep is a backend for creating stateful AI apps with background tasks and long-term memory easily. - version: 0.4.0 -externalDocs: - url: https://docs.julep.ai - description: Julep API documentation -tags: [] -paths: - /agents: - get: - operationId: AgentsRoute_list - description: List Agents (paginated) - parameters: - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Agents.Agent' - required: - - items - post: - operationId: AgentsRoute_create - description: Create a new Agent - parameters: [] - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.CreateAgentRequest' - /agents/{id}: - post: - operationId: AgentsRoute_createOrUpdate - description: Create or update an Agent - parameters: - - $ref: '#/components/parameters/Agents.CreateOrUpdateAgentRequest.id' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.UpdateAgentRequest' - put: - operationId: AgentsRoute_update - description: Update an existing Agent by id (overwrites existing values; use PATCH for merging instead) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.UpdateAgentRequest' - patch: - operationId: AgentsRoute_patch - description: Update an existing Agent by id (merges with existing values) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.PatchAgentRequest' - delete: - operationId: AgentsRoute_delete - description: Delete Agent by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - get: - operationId: AgentsRoute_get - description: Get an Agent by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.Agent' - /agents/{id}/docs: - get: - operationId: AgentDocsRoute_list - description: List Docs owned by an Agent - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Docs.Doc' - required: - - items - post: - operationId: AgentDocsRoute_create - description: Create a Doc for this Agent - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.CreateDocRequest' - /agents/{id}/docs/{child_id}: - delete: - operationId: AgentDocsRoute_delete - description: Delete a Doc for this Agent - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be deleted - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - /agents/{id}/search: - post: - operationId: AgentsDocsSearchRoute_search - description: Search Docs owned by an Agent - parameters: - - name: id - in: path - required: true - description: ID of the parent - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.DocSearchResponse' - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - body: - anyOf: - - $ref: '#/components/schemas/Docs.VectorDocSearchRequest' - - $ref: '#/components/schemas/Docs.TextOnlyDocSearchRequest' - - $ref: '#/components/schemas/Docs.HybridDocSearchRequest' - required: - - body - /agents/{id}/tasks: - get: - operationId: TasksRoute_list - description: List tasks (paginated) - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Tasks.Task' - required: - - items - post: - operationId: TasksRoute_create - description: Create a new task - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - text/x-yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - text/yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - application/json: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - /agents/{id}/tasks/{child_id}: - put: - operationId: TasksRoute_update - description: Update an existing task (overwrite existing values) - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be updated - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Tasks.UpdateTaskRequest' - patch: - operationId: TasksRoute_patch - description: Update an existing task (merges with existing values) - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be patched - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Tasks.PatchTaskRequest' - delete: - operationId: TasksRoute_delete - description: Delete a task by its id - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be deleted - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - /agents/{id}/tools: - get: - operationId: AgentToolsRoute_list - description: List tools of the given agent - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Tools.Tool' - required: - - items - post: - operationId: AgentToolsRoute_create - description: Create a new tool for this agent - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Agents.CreateAgentRequest' - /agents/{id}/tools/{child_id}: - put: - operationId: AgentToolsRoute_update - description: Update an existing tool (overwrite existing values) - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be updated - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Tools.UpdateToolRequest' - patch: - operationId: AgentToolsRoute_patch - description: Update an existing tool (merges with existing values) - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be patched - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Tools.PatchToolRequest' - delete: - operationId: AgentToolsRoute_delete - description: Delete an existing tool by id - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be deleted - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - /agents/{parent_id}/tasks/{id}: - post: - operationId: TasksCreateOrUpdateRoute_createOrUpdate - description: Create or update a task - parameters: - - name: parent_id - in: path - required: true - description: ID of the agent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Tasks.CreateOrUpdateTaskRequest.id' - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - text/x-yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - text/yaml: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - application/json: - schema: - $ref: '#/components/schemas/Tasks.CreateTaskRequest' - /docs/{id}: - get: - operationId: IndividualDocsRoute_get - description: Get Doc by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.Doc' - /embed: - post: - operationId: EmbedRoute_embed - description: Embed a query for search - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.EmbedQueryResponse' - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - body: - $ref: '#/components/schemas/Docs.EmbedQueryRequest' - required: - - body - /executions: - post: - operationId: ExecutionsRoute_resumeWithTaskToken - description: Resume an execution with a task token - parameters: - - name: task_token - in: query - required: true - description: A Task Token is a unique identifier for a specific Task Execution. - schema: - type: string - explode: false - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Executions.TaskTokenResumeExecutionRequest' - description: Request to resume an execution with a task token - security: - - {} - /executions/{id}: - get: - operationId: ExecutionsRoute_get - description: Get an Execution by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Executions.Execution' - put: - operationId: ExecutionsRoute_update - description: Update an existing Execution - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Executions.UpdateExecutionRequest' - /executions/{id}/transitions: - get: - operationId: ExecutionTransitionsRoute_list - description: List the Transitions of an Execution by id - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - type: object - properties: - transitions: - type: array - items: - $ref: '#/components/schemas/Executions.Transition' - required: - - transitions - required: - - items - /executions/{id}/transitions.stream: - get: - operationId: ExecutionTransitionsStreamRoute_stream - description: Stream events emitted by the given execution - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - name: next_token - in: query - required: true - description: Next page token - schema: - type: string - nullable: true - default: null - explode: false - responses: - '200': - description: The request has succeeded. - content: - text/event-stream: - schema: - $ref: '#/components/schemas/Executions.TransitionEvent' - /jobs/{id}: - get: - operationId: JobRoute_get - description: Get the status of an existing Job by its id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Jobs.JobStatus' - /sessions: - get: - operationId: SessionsRoute_list - description: List sessions (paginated) - parameters: - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Sessions.Session' - required: - - items - post: - operationId: SessionsRoute_create - description: Create a new session - parameters: [] - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Sessions.CreateSessionRequest' - /sessions/{id}: - post: - operationId: SessionsRoute_createOrUpdate - description: Create or update a session - parameters: - - $ref: '#/components/parameters/Sessions.CreateOrUpdateSessionRequest.id' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Sessions.CreateSessionRequest' - put: - operationId: SessionsRoute_update - description: Update an existing session by its id (overwrites all existing values) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Sessions.UpdateSessionRequest' - patch: - operationId: SessionsRoute_patch - description: Update an existing session by its id (merges with existing values) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Sessions.PatchSessionRequest' - delete: - operationId: SessionsRoute_delete - description: Delete a session by its id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - get: - operationId: SessionsRoute_get - description: Get a session by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Sessions.Session' - /sessions/{id}/chat: - post: - operationId: ChatRoute_generate - description: Generate a response from the model - parameters: - - name: id - in: path - required: true - description: The session ID - schema: - $ref: '#/components/schemas/Common.uuid' - - name: x-custom-api-key - in: header - required: false - description: Custom API key - schema: - type: string - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - anyOf: - - $ref: '#/components/schemas/Chat.ChunkChatResponse' - - $ref: '#/components/schemas/Chat.MessageChatResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Chat.ChatInput' - description: Request to generate a response from the model - /sessions/{id}/history: - delete: - operationId: HistoryRoute_delete - description: Clear the history of a Session (resets the Session) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - get: - operationId: HistoryRoute_history - description: Get history of a Session - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Entries.History' - /tasks/{id}/executions: - post: - operationId: TaskExecutionsRoute_create - description: Create an execution for the given task - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Executions.CreateExecutionRequest' - get: - operationId: TaskExecutionsRoute_list - description: List executions of the given task - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Executions.Execution' - required: - - items - /users: - get: - operationId: UsersRoute_list - description: List users (paginated) - parameters: - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Users.User' - required: - - items - post: - operationId: UsersRoute_create - description: Create a new user - parameters: [] - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Users.CreateUserRequest' - /users/{id}: - post: - operationId: UsersRoute_createOrUpdate - description: Create or update a user - parameters: - - $ref: '#/components/parameters/Users.CreateOrUpdateUserRequest' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Users.CreateUserRequest' - put: - operationId: UsersRoute_update - description: Update an existing user by id (overwrite existing values) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Users.UpdateUserRequest' - patch: - operationId: UsersRoute_patch - description: Update an existing user by id (merge with existing values) - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceUpdatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Users.PatchUserRequest' - delete: - operationId: UsersRoute_delete - description: Delete a user by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - get: - operationId: UsersRoute_get - description: Get a user by id - parameters: - - name: id - in: path - required: true - description: ID of the resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Users.User' - /users/{id}/docs: - get: - operationId: UserDocsRoute_list - description: List Docs owned by a User - parameters: - - name: id - in: path - required: true - description: ID of parent - schema: - $ref: '#/components/schemas/Common.uuid' - - $ref: '#/components/parameters/Common.PaginationOptions.limit' - - $ref: '#/components/parameters/Common.PaginationOptions.offset' - - $ref: '#/components/parameters/Common.PaginationOptions.sort_by' - - $ref: '#/components/parameters/Common.PaginationOptions.direction' - - $ref: '#/components/parameters/Common.PaginationOptions.metadata_filter' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Docs.Doc' - required: - - items - post: - operationId: UserDocsRoute_create - description: Create a Doc for this User - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '201': - description: The request has succeeded and a new resource has been created as a result. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceCreatedResponse' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.CreateDocRequest' - /users/{id}/docs/{child_id}: - delete: - operationId: UserDocsRoute_delete - description: Delete a Doc for this User - parameters: - - name: id - in: path - required: true - description: ID of parent resource - schema: - $ref: '#/components/schemas/Common.uuid' - - name: child_id - in: path - required: true - description: ID of the resource to be deleted - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '202': - description: The request has been accepted for processing, but processing has not yet completed. - content: - application/json: - schema: - $ref: '#/components/schemas/Common.ResourceDeletedResponse' - /users/{id}/search: - post: - operationId: UserDocsSearchRoute_search - description: Search Docs owned by a User - parameters: - - name: id - in: path - required: true - description: ID of the parent - schema: - $ref: '#/components/schemas/Common.uuid' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Docs.DocSearchResponse' - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - body: - anyOf: - - $ref: '#/components/schemas/Docs.VectorDocSearchRequest' - - $ref: '#/components/schemas/Docs.TextOnlyDocSearchRequest' - - $ref: '#/components/schemas/Docs.HybridDocSearchRequest' - required: - - body -security: - - ApiKeyAuth: [] - - ApiKeyAuth_: [] -components: - parameters: - Agents.CreateOrUpdateAgentRequest.id: - name: id - in: path - required: true - schema: - $ref: '#/components/schemas/Common.uuid' - Common.PaginationOptions.direction: - name: direction - in: query - required: true - description: Sort direction - schema: - type: string - enum: - - asc - - desc - default: asc - explode: false - Common.PaginationOptions.limit: - name: limit - in: query - required: true - description: Limit the number of items returned - schema: - $ref: '#/components/schemas/Common.limit' - default: 100 - explode: false - Common.PaginationOptions.metadata_filter: - name: metadata_filter - in: query - required: true - description: Object to filter results by metadata - schema: - type: object - additionalProperties: - anyOf: - - type: number - - type: string - - type: boolean - nullable: true - explode: false - Common.PaginationOptions.offset: - name: offset - in: query - required: true - description: Offset the items returned - schema: - $ref: '#/components/schemas/Common.offset' - default: 0 - explode: false - Common.PaginationOptions.sort_by: - name: sort_by - in: query - required: true - description: Sort by a field - schema: - type: string - enum: - - created_at - - updated_at - default: created_at - explode: false - Sessions.CreateOrUpdateSessionRequest.id: - name: id - in: path - required: true - schema: - $ref: '#/components/schemas/Common.uuid' - Tasks.CreateOrUpdateTaskRequest.id: - name: id - in: path - required: true - schema: - $ref: '#/components/schemas/Common.uuid' - Users.CreateOrUpdateUserRequest: - name: id - in: path - required: true - schema: - $ref: '#/components/schemas/Common.uuid' - schemas: - Agents.Agent: - type: object - required: - - id - - created_at - - updated_at - - name - - about - - model - - instructions - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - metadata: - type: object - additionalProperties: {} - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the agent - default: '' - about: - type: string - description: About the agent - default: '' - model: - type: string - description: Model name to use (gpt-4-turbo, gemini-nano etc) - default: '' - instructions: - anyOf: - - type: string - - type: array - items: - type: string - description: Instructions for the agent - default: [] - default_settings: - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - description: Default settings for all sessions created by this agent - Agents.CreateAgentRequest: - type: object - required: - - name - - about - - model - - instructions - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the agent - default: '' - about: - type: string - description: About the agent - default: '' - model: - type: string - description: Model name to use (gpt-4-turbo, gemini-nano etc) - default: '' - instructions: - anyOf: - - type: string - - type: array - items: - type: string - description: Instructions for the agent - default: [] - default_settings: - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - description: Default settings for all sessions created by this agent - description: Payload for creating a agent (and associated documents) - Agents.CreateOrUpdateAgentRequest: - type: object - required: - - id - - name - - about - - model - - instructions - properties: - id: - $ref: '#/components/schemas/Common.uuid' - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the agent - default: '' - about: - type: string - description: About the agent - default: '' - model: - type: string - description: Model name to use (gpt-4-turbo, gemini-nano etc) - default: '' - instructions: - anyOf: - - type: string - - type: array - items: - type: string - description: Instructions for the agent - default: [] - default_settings: - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - description: Default settings for all sessions created by this agent - allOf: - - $ref: '#/components/schemas/Agents.CreateAgentRequest' - Agents.PatchAgentRequest: - type: object - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the agent - default: '' - about: - type: string - description: About the agent - default: '' - model: - type: string - description: Model name to use (gpt-4-turbo, gemini-nano etc) - default: '' - instructions: - anyOf: - - type: string - - type: array - items: - type: string - description: Instructions for the agent - default: [] - default_settings: - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - description: Default settings for all sessions created by this agent - description: Payload for patching a agent - Agents.UpdateAgentRequest: - type: object - required: - - name - - about - - model - - instructions - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the agent - default: '' - about: - type: string - description: About the agent - default: '' - model: - type: string - description: Model name to use (gpt-4-turbo, gemini-nano etc) - default: '' - instructions: - anyOf: - - type: string - - type: array - items: - type: string - description: Instructions for the agent - default: [] - default_settings: - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - description: Default settings for all sessions created by this agent - description: Payload for updating a agent - Chat.BaseChatOutput: - type: object - required: - - index - - finish_reason - properties: - index: - type: integer - format: uint32 - finish_reason: - allOf: - - $ref: '#/components/schemas/Chat.FinishReason' - description: The reason the model stopped generating tokens - default: stop - logprobs: - allOf: - - $ref: '#/components/schemas/Chat.LogProbResponse' - description: The log probabilities of tokens - Chat.BaseChatResponse: - type: object - required: - - jobs - - docs - - created_at - - id - properties: - usage: - allOf: - - $ref: '#/components/schemas/Chat.CompetionUsage' - description: Usage statistics for the completion request - jobs: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - description: Background job IDs that may have been spawned from this interaction. - default: [] - readOnly: true - docs: - type: array - items: - $ref: '#/components/schemas/Docs.DocReference' - description: Documents referenced for this request (for citation purposes). - default: [] - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - Chat.BaseTokenLogProb: - type: object - required: - - token - - logprob - properties: - token: - type: string - logprob: - type: number - format: float - description: The log probability of the token - bytes: - type: array - items: - type: integer - format: uint16 - Chat.ChatInput: - type: object - required: - - remember - - recall - - save - - stream - - stop - properties: - remember: - type: boolean - description: 'DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release)' - default: false - readOnly: true - recall: - type: boolean - description: Whether previous memories and docs should be recalled or not - default: true - save: - type: boolean - description: Whether this interaction should be stored in the session history or not - default: true - model: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Identifier of the model to be used - stream: - type: boolean - description: Indicates if the server should stream the response as it's generated - default: false - stop: - type: array - items: - type: string - maxItems: 4 - description: Up to 4 sequences where the API will stop generating further tokens. - default: [] - seed: - type: integer - format: int16 - minimum: -1 - maximum: 1000 - description: If specified, the system will make a best effort to sample deterministically for that particular seed value - max_tokens: - type: integer - format: uint32 - minimum: 1 - description: The maximum number of tokens to generate in the chat completion - logit_bias: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.logit_bias' - description: Modify the likelihood of specified tokens appearing in the completion - response_format: - anyOf: - - $ref: '#/components/schemas/Chat.SimpleCompletionResponseFormat' - - $ref: '#/components/schemas/Chat.SchemaCompletionResponseFormat' - description: Response format (set to `json_object` to restrict output to JSON) - agent: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - repetition_penalty: - type: number - format: float - minimum: 0 - maximum: 2 - description: Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - length_penalty: - type: number - format: float - minimum: 0 - maximum: 2 - description: Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - min_p: - type: number - format: float - minimum: 0 - maximum: 1 - description: Minimum probability compared to leading token to be considered - frequency_penalty: - type: number - format: float - minimum: -2 - maximum: 2 - description: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - presence_penalty: - type: number - format: float - minimum: -2 - maximum: 2 - description: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - temperature: - type: number - format: float - minimum: 0 - maximum: 5 - description: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - top_p: - type: number - format: float - minimum: 0 - maximum: 1 - description: Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - allOf: - - $ref: '#/components/schemas/Chat.ChatInputData' - Chat.ChatInputData: - type: object - required: - - messages - - tools - properties: - messages: - type: array - items: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - type: string - - type: array - items: - type: string - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - continue: - type: boolean - description: Whether to continue this message or return a new one - minItems: 1 - description: A list of new input messages comprising the conversation so far. - tools: - type: array - items: - $ref: '#/components/schemas/Tools.Tool' - description: (Advanced) List of tools that are provided in addition to agent's default set of tools. - default: [] - tool_choice: - anyOf: - - type: string - enum: - - auto - - none - - $ref: '#/components/schemas/Tools.NamedToolChoice' - description: Can be one of existing tools given to the agent earlier or the ones provided in this request. - Chat.ChatOutputChunk: - type: object - required: - - delta - properties: - delta: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - type: string - - type: array - items: - type: string - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - continue: - type: boolean - description: Whether to continue this message or return a new one - description: The message generated by the model - allOf: - - $ref: '#/components/schemas/Chat.BaseChatOutput' - description: Streaming chat completion output - Chat.ChatSettings: - type: object - required: - - stream - - stop - properties: - model: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Identifier of the model to be used - stream: - type: boolean - description: Indicates if the server should stream the response as it's generated - default: false - stop: - type: array - items: - type: string - maxItems: 4 - description: Up to 4 sequences where the API will stop generating further tokens. - default: [] - seed: - type: integer - format: int16 - minimum: -1 - maximum: 1000 - description: If specified, the system will make a best effort to sample deterministically for that particular seed value - max_tokens: - type: integer - format: uint32 - minimum: 1 - description: The maximum number of tokens to generate in the chat completion - logit_bias: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.logit_bias' - description: Modify the likelihood of specified tokens appearing in the completion - response_format: - anyOf: - - $ref: '#/components/schemas/Chat.SimpleCompletionResponseFormat' - - $ref: '#/components/schemas/Chat.SchemaCompletionResponseFormat' - description: Response format (set to `json_object` to restrict output to JSON) - agent: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - allOf: - - $ref: '#/components/schemas/Chat.DefaultChatSettings' - Chat.ChunkChatResponse: - type: object - required: - - choices - properties: - choices: - type: array - items: - $ref: '#/components/schemas/Chat.ChatOutputChunk' - description: The deltas generated by the model - allOf: - - $ref: '#/components/schemas/Chat.BaseChatResponse' - Chat.CompetionUsage: - type: object - properties: - completion_tokens: - type: integer - format: uint32 - description: Number of tokens in the generated completion - readOnly: true - prompt_tokens: - type: integer - format: uint32 - description: Number of tokens in the prompt - readOnly: true - total_tokens: - type: integer - format: uint32 - description: Total number of tokens used in the request (prompt + completion) - readOnly: true - description: Usage statistics for the completion request - Chat.DefaultChatSettings: - type: object - properties: - repetition_penalty: - type: number - format: float - minimum: 0 - maximum: 2 - description: Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - length_penalty: - type: number - format: float - minimum: 0 - maximum: 2 - description: Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - min_p: - type: number - format: float - minimum: 0 - maximum: 1 - description: Minimum probability compared to leading token to be considered - allOf: - - $ref: '#/components/schemas/Chat.OpenAISettings' - description: Default settings for the chat session (also used by the agent) - Chat.FinishReason: - type: string - enum: - - stop - - length - - content_filter - - tool_calls - description: |- - The reason the model stopped generating tokens. This will be `stop` - if the model hit a natural stop point or a provided stop sequence, - `length` if the maximum number of tokens specified in the request - was reached, `content_filter` if content was omitted due to a flag - from our content filters, `tool_calls` if the model called a tool. - Chat.LogProbResponse: - type: object - required: - - content - properties: - content: - type: array - items: - $ref: '#/components/schemas/Chat.TokenLogProb' - nullable: true - description: The log probabilities of the tokens - Chat.MessageChatResponse: - type: object - required: - - choices - properties: - choices: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Chat.SingleChatOutput' - - $ref: '#/components/schemas/Chat.MultipleChatOutput' - description: The deltas generated by the model - allOf: - - $ref: '#/components/schemas/Chat.BaseChatResponse' - Chat.MultipleChatOutput: - type: object - required: - - messages - properties: - messages: - type: array - items: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - type: string - - type: array - items: - type: string - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - tool_calls: - type: array - items: - $ref: '#/components/schemas/Tools.ChosenToolCall' - nullable: true - description: Tool calls generated by the model. - default: [] - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - minItems: 1 - readOnly: true - allOf: - - $ref: '#/components/schemas/Chat.BaseChatOutput' - description: The output returned by the model. Note that, depending on the model provider, they might return more than one message. - Chat.OpenAISettings: - type: object - properties: - frequency_penalty: - type: number - format: float - minimum: -2 - maximum: 2 - description: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - presence_penalty: - type: number - format: float - minimum: -2 - maximum: 2 - description: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - temperature: - type: number - format: float - minimum: 0 - maximum: 5 - description: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - top_p: - type: number - format: float - minimum: 0 - maximum: 1 - description: Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - Chat.SchemaCompletionResponseFormat: - type: object - required: - - type - - json_schema - properties: - type: - type: string - enum: - - json_schema - description: The format of the response - default: json_schema - json_schema: - type: object - additionalProperties: {} - description: The schema of the response - Chat.SimpleCompletionResponseFormat: - type: object - required: - - type - properties: - type: - type: string - enum: - - text - - json_object - description: The format of the response - default: text - Chat.SingleChatOutput: - type: object - required: - - message - properties: - message: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - type: string - - type: array - items: - type: string - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - tool_calls: - type: array - items: - $ref: '#/components/schemas/Tools.ChosenToolCall' - nullable: true - description: Tool calls generated by the model. - default: [] - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - allOf: - - $ref: '#/components/schemas/Chat.BaseChatOutput' - description: The output returned by the model. Note that, depending on the model provider, they might return more than one message. - Chat.TokenLogProb: - type: object - required: - - top_logprobs - properties: - top_logprobs: - type: array - items: - $ref: '#/components/schemas/Chat.BaseTokenLogProb' - minItems: 1 - description: The log probabilities of the tokens - readOnly: true - allOf: - - $ref: '#/components/schemas/Chat.BaseTokenLogProb' - Common.JinjaTemplate: - type: string - description: A valid jinja template. - Common.PyExpression: - type: string - description: A simple python expression compatible with SimpleEval. - Common.ResourceCreatedResponse: - type: object - required: - - id - - created_at - - jobs - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: ID of created resource - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - jobs: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - description: IDs (if any) of jobs created as part of this request - default: [] - readOnly: true - Common.ResourceDeletedResponse: - type: object - required: - - id - - deleted_at - - jobs - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: ID of deleted resource - deleted_at: - type: string - format: date-time - description: When this resource was deleted as UTC date-time - readOnly: true - jobs: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - description: IDs (if any) of jobs created as part of this request - default: [] - readOnly: true - Common.ResourceUpdatedResponse: - type: object - required: - - id - - updated_at - - jobs - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: ID of updated resource - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - jobs: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - description: IDs (if any) of jobs created as part of this request - default: [] - readOnly: true - Common.identifierSafeUnicode: - type: string - maxLength: 120 - pattern: ^[\p{L}\p{Nl}\p{Pattern_Syntax}\p{Pattern_White_Space}]+[\p{ID_Start}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Pattern_Syntax}\p{Pattern_White_Space}]*$ - description: |- - For Unicode character safety - See: https://unicode.org/reports/tr31/ - See: https://www.unicode.org/reports/tr39/#Identifier_Characters - Common.limit: - type: integer - format: uint16 - minimum: 1 - maximum: 1000 - exclusiveMaximum: true - description: Limit the number of results - Common.logit_bias: - type: number - format: float - minimum: -100 - maximum: 100 - Common.offset: - type: integer - format: uint32 - minimum: 0 - description: Offset to apply to the results - Common.uuid: - type: string - format: uuid - Common.validPythonIdentifier: - type: string - maxLength: 40 - pattern: ^[^\W0-9]\w*$ - description: Valid python identifier names - Docs.BaseDocSearchRequest: - type: object - required: - - limit - - lang - properties: - limit: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - default: 10 - lang: - type: string - enum: - - en-US - description: The language to be used for text-only search. Support for other languages coming soon. - default: en-US - Docs.CreateDocRequest: - type: object - required: - - title - - content - properties: - metadata: - type: object - additionalProperties: {} - title: - type: string - maxLength: 800 - description: Title describing what this document contains - content: - anyOf: - - type: string - - type: array - items: - type: string - description: Contents of the document - description: Payload for creating a doc - Docs.Doc: - type: object - required: - - id - - created_at - - title - - content - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - metadata: - type: object - additionalProperties: {} - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - title: - type: string - maxLength: 800 - description: Title describing what this document contains - content: - anyOf: - - type: string - - type: array - items: - type: string - description: Contents of the document - embeddings: - anyOf: - - type: array - items: - type: number - format: float - - type: array - items: - type: array - items: - type: number - format: float - description: Embeddings for the document - readOnly: true - Docs.DocOwner: - type: object - required: - - id - - role - properties: - id: - anyOf: - - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - role: - type: string - enum: - - user - - agent - Docs.DocReference: - type: object - required: - - owner - - id - - snippets - - distance - properties: - owner: - allOf: - - $ref: '#/components/schemas/Docs.DocOwner' - description: The owner of this document. - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - description: ID of the document - title: - type: string - snippets: - type: array - items: - $ref: '#/components/schemas/Docs.Snippet' - minItems: 1 - distance: - type: number - nullable: true - default: null - Docs.DocSearchResponse: - type: object - required: - - docs - - time - properties: - docs: - type: array - items: - $ref: '#/components/schemas/Docs.DocReference' - description: The documents that were found - time: - type: number - minimum: 0 - exclusiveMinimum: true - description: The time taken to search in seconds - Docs.EmbedQueryRequest: - type: object - required: - - text - properties: - text: - anyOf: - - type: string - - type: array - items: - type: string - description: Text or texts to embed - Docs.EmbedQueryResponse: - type: object - required: - - vectors - properties: - vectors: - type: array - items: - type: array - items: - type: number - description: The embedded vectors - Docs.HybridDocSearchRequest: - type: object - required: - - confidence - - alpha - - text - - vector - properties: - confidence: - type: number - minimum: 0 - maximum: 1 - description: The confidence cutoff level - default: 0.5 - alpha: - type: number - minimum: 0 - maximum: 1 - description: The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; - default: 0.75 - text: - type: string - description: Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. - vector: - type: array - items: - type: number - description: Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. - allOf: - - $ref: '#/components/schemas/Docs.BaseDocSearchRequest' - Docs.Snippet: - type: object - required: - - index - - content - properties: - index: - type: integer - format: uint16 - content: - type: string - Docs.TextOnlyDocSearchRequest: - type: object - required: - - text - properties: - text: - type: string - description: Text to use in the search. - allOf: - - $ref: '#/components/schemas/Docs.BaseDocSearchRequest' - Docs.VectorDocSearchRequest: - type: object - required: - - confidence - - vector - properties: - confidence: - type: number - minimum: 0 - maximum: 1 - description: The confidence cutoff level - default: 0.5 - vector: - type: array - items: - type: number - description: Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. - allOf: - - $ref: '#/components/schemas/Docs.BaseDocSearchRequest' - Entries.BaseEntry: - type: object - required: - - role - - name - - content - - source - - tokenizer - - token_count - - timestamp - properties: - role: - $ref: '#/components/schemas/Entries.ChatMLRole' - name: - type: string - nullable: true - default: null - content: - anyOf: - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - - $ref: '#/components/schemas/Tools.Tool' - - $ref: '#/components/schemas/Tools.ChosenToolCall' - - type: string - - $ref: '#/components/schemas/Tools.ToolResponse' - - type: array - items: - anyOf: - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - type: string - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - - $ref: '#/components/schemas/Tools.Tool' - - $ref: '#/components/schemas/Tools.ChosenToolCall' - - type: string - - $ref: '#/components/schemas/Tools.ToolResponse' - source: - type: string - enum: - - api_request - - api_response - - tool_response - - internal - - summarizer - - meta - tokenizer: - type: string - token_count: - type: integer - format: uint16 - timestamp: - type: number - minimum: 0 - description: This is the time that this event refers to. - Entries.ChatMLRole: - type: string - enum: - - user - - assistant - - system - - function - - function_response - - function_call - - auto - description: ChatML role (system|assistant|user|function_call|function|function_response|auto) - Entries.Entry: - type: object - required: - - created_at - - id - properties: - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - allOf: - - $ref: '#/components/schemas/Entries.BaseEntry' - Entries.History: - type: object - required: - - entries - - relations - - session_id - - created_at - properties: - entries: - type: array - items: - $ref: '#/components/schemas/Entries.Entry' - relations: - type: array - items: - $ref: '#/components/schemas/Entries.Relation' - session_id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - Entries.ImageDetail: - type: string - enum: - - low - - high - - auto - description: Image detail level - Entries.Relation: - type: object - required: - - head - - relation - - tail - properties: - head: - $ref: '#/components/schemas/Common.uuid' - relation: - type: string - tail: - $ref: '#/components/schemas/Common.uuid' - Executions.CreateExecutionRequest: - type: object - required: - - input - properties: - input: - type: object - additionalProperties: {} - description: The input to the execution - output: - description: The output of the execution if it succeeded - error: - type: string - description: The error of the execution if it failed - metadata: - type: object - additionalProperties: {} - description: Payload for creating an execution - Executions.Execution: - type: object - required: - - task_id - - status - - input - - created_at - - updated_at - - id - properties: - task_id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - description: The ID of the task that the execution is running - status: - type: string - enum: - - queued - - starting - - running - - awaiting_input - - succeeded - - failed - - cancelled - description: The status of the execution - readOnly: true - input: - type: object - additionalProperties: {} - description: The input to the execution - output: - description: The output of the execution if it succeeded - error: - type: string - description: The error of the execution if it failed - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - metadata: - type: object - additionalProperties: {} - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - Executions.ResumeExecutionRequest: - type: object - required: - - status - properties: - status: - type: string - enum: - - running - default: running - input: - type: object - additionalProperties: {} - description: The input to resume the execution with - allOf: - - $ref: '#/components/schemas/Executions.UpdateExecutionRequest' - Executions.StopExecutionRequest: - type: object - required: - - status - - reason - properties: - status: - type: string - enum: - - cancelled - default: cancelled - reason: - type: string - nullable: true - description: The reason for stopping the execution - default: null - allOf: - - $ref: '#/components/schemas/Executions.UpdateExecutionRequest' - Executions.TaskTokenResumeExecutionRequest: - type: object - required: - - status - properties: - status: - type: string - enum: - - running - default: running - input: - type: object - additionalProperties: {} - description: The input to resume the execution with - Executions.Transition: - type: object - required: - - execution_id - - current - - next - - id - properties: - execution_id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - current: - allOf: - - $ref: '#/components/schemas/Executions.TransitionTarget' - readOnly: true - next: - type: object - allOf: - - $ref: '#/components/schemas/Executions.TransitionTarget' - nullable: true - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - metadata: - type: object - additionalProperties: {} - allOf: - - $ref: '#/components/schemas/Executions.TransitionEvent' - Executions.TransitionEvent: - type: object - required: - - type - - output - - created_at - - updated_at - properties: - type: - type: string - enum: - - init - - init_branch - - finish - - finish_branch - - wait - - resume - - error - - step - - cancelled - readOnly: true - output: - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - Executions.TransitionTarget: - type: object - required: - - workflow - - step - properties: - workflow: - $ref: '#/components/schemas/Common.identifierSafeUnicode' - step: - type: integer - format: uint16 - Executions.UpdateExecutionRequest: - type: object - required: - - status - properties: - status: - type: string - enum: - - queued - - starting - - running - - awaiting_input - - succeeded - - failed - - cancelled - discriminator: - propertyName: status - mapping: - cancelled: '#/components/schemas/Executions.StopExecutionRequest' - running: '#/components/schemas/Executions.ResumeExecutionRequest' - Jobs.JobState: - type: string - enum: - - pending - - in_progress - - retrying - - succeeded - - aborted - - failed - - unknown - description: 'Current state (one of: pending, in_progress, retrying, succeeded, aborted, failed)' - Jobs.JobStatus: - type: object - required: - - id - - created_at - - updated_at - - name - - reason - - has_progress - - progress - - state - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the job - default: '' - reason: - type: string - description: Reason for the current state of the job - default: '' - has_progress: - type: boolean - description: Whether this Job supports progress updates - default: false - progress: - type: number - format: float - minimum: 0 - maximum: 100 - description: Progress percentage - default: 0 - state: - allOf: - - $ref: '#/components/schemas/Jobs.JobState' - description: Current state of the job - default: pending - Sessions.ContextOverflowType: - type: string - enum: - - truncate - - adaptive - Sessions.CreateOrUpdateSessionRequest: - type: object - required: - - id - - situation - - render_templates - - token_budget - - context_overflow - - forward_tool_results - properties: - id: - $ref: '#/components/schemas/Common.uuid' - user: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: User ID of user associated with this session - users: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - agent: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Agent ID of agent associated with this session - agents: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - situation: - type: string - description: A specific situation that sets the background for this session - default: |- - {%- if agent.name -%} - You are {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if user -%} - You are talking to a user - {%- if user.name -%}{{" "}} and their name is {{user.name}} - {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} - {%- endif -%} - {%- endif -%} - - {{" - - "}} - - {%- if agent.instructions -%} - Instructions:{{" - "}} - {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} - {%- else -%} - {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{" - "}} - {%- endif -%} - - {%- if tools -%} - Tools:{{" - "}} - {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} - {%- endfor -%} - {{" - - "}} - {%- endif -%} - - {%- if docs -%} - Relevant documents:{{" - "}} - {%- for doc in docs -%} - {{doc.title}}{{" - "}} - {%- if doc.content is string -%} - {{doc.content}}{{" - "}} - {%- else -%} - {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{"---"}} - {%- endfor -%} - {%- endif -%} - render_templates: - type: boolean - description: Render system and assistant message content as jinja templates - default: true - token_budget: - type: integer - format: uint16 - nullable: true - description: Threshold value for the adaptive context functionality - default: null - context_overflow: - oneOf: - - $ref: '#/components/schemas/Sessions.ContextOverflowType' - nullable: true - description: Action to start on context window overflow - default: null - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be sent back to the model as the model's input. - If a tool call is not made, the model's output will be returned as is. - default: null - metadata: - type: object - additionalProperties: {} - allOf: - - $ref: '#/components/schemas/Sessions.CreateSessionRequest' - Sessions.CreateSessionRequest: - type: object - required: - - situation - - render_templates - - token_budget - - context_overflow - - forward_tool_results - properties: - user: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: User ID of user associated with this session - users: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - agent: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Agent ID of agent associated with this session - agents: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - situation: - type: string - description: A specific situation that sets the background for this session - default: |- - {%- if agent.name -%} - You are {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if user -%} - You are talking to a user - {%- if user.name -%}{{" "}} and their name is {{user.name}} - {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} - {%- endif -%} - {%- endif -%} - - {{" - - "}} - - {%- if agent.instructions -%} - Instructions:{{" - "}} - {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} - {%- else -%} - {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{" - "}} - {%- endif -%} - - {%- if tools -%} - Tools:{{" - "}} - {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} - {%- endfor -%} - {{" - - "}} - {%- endif -%} - - {%- if docs -%} - Relevant documents:{{" - "}} - {%- for doc in docs -%} - {{doc.title}}{{" - "}} - {%- if doc.content is string -%} - {{doc.content}}{{" - "}} - {%- else -%} - {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{"---"}} - {%- endfor -%} - {%- endif -%} - render_templates: - type: boolean - description: Render system and assistant message content as jinja templates - default: true - token_budget: - type: integer - format: uint16 - nullable: true - description: Threshold value for the adaptive context functionality - default: null - context_overflow: - oneOf: - - $ref: '#/components/schemas/Sessions.ContextOverflowType' - nullable: true - description: Action to start on context window overflow - default: null - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be sent back to the model as the model's input. - If a tool call is not made, the model's output will be returned as is. - default: null - metadata: - type: object - additionalProperties: {} - description: Payload for creating a session - Sessions.MultiAgentMultiUserSession: - type: object - required: - - agents - - users - properties: - agents: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - minItems: 2 - users: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - minItems: 2 - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.MultiAgentNoUserSession: - type: object - required: - - agents - properties: - agents: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - minItems: 2 - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.MultiAgentSingleUserSession: - type: object - required: - - agents - - user - properties: - agents: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - minItems: 2 - user: - $ref: '#/components/schemas/Common.uuid' - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.PatchSessionRequest: - type: object - properties: - situation: - type: string - description: A specific situation that sets the background for this session - default: |- - {%- if agent.name -%} - You are {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if user -%} - You are talking to a user - {%- if user.name -%}{{" "}} and their name is {{user.name}} - {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} - {%- endif -%} - {%- endif -%} - - {{" - - "}} - - {%- if agent.instructions -%} - Instructions:{{" - "}} - {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} - {%- else -%} - {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{" - "}} - {%- endif -%} - - {%- if tools -%} - Tools:{{" - "}} - {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} - {%- endfor -%} - {{" - - "}} - {%- endif -%} - - {%- if docs -%} - Relevant documents:{{" - "}} - {%- for doc in docs -%} - {{doc.title}}{{" - "}} - {%- if doc.content is string -%} - {{doc.content}}{{" - "}} - {%- else -%} - {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{"---"}} - {%- endfor -%} - {%- endif -%} - render_templates: - type: boolean - description: Render system and assistant message content as jinja templates - default: true - token_budget: - type: integer - format: uint16 - nullable: true - description: Threshold value for the adaptive context functionality - default: null - context_overflow: - oneOf: - - $ref: '#/components/schemas/Sessions.ContextOverflowType' - nullable: true - description: Action to start on context window overflow - default: null - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be sent back to the model as the model's input. - If a tool call is not made, the model's output will be returned as is. - default: null - metadata: - type: object - additionalProperties: {} - description: Payload for patching a session - Sessions.Session: - type: object - required: - - situation - - summary - - render_templates - - token_budget - - context_overflow - - forward_tool_results - - id - - created_at - - updated_at - properties: - situation: - type: string - description: A specific situation that sets the background for this session - default: |- - {%- if agent.name -%} - You are {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if user -%} - You are talking to a user - {%- if user.name -%}{{" "}} and their name is {{user.name}} - {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} - {%- endif -%} - {%- endif -%} - - {{" - - "}} - - {%- if agent.instructions -%} - Instructions:{{" - "}} - {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} - {%- else -%} - {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{" - "}} - {%- endif -%} - - {%- if tools -%} - Tools:{{" - "}} - {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} - {%- endfor -%} - {{" - - "}} - {%- endif -%} - - {%- if docs -%} - Relevant documents:{{" - "}} - {%- for doc in docs -%} - {{doc.title}}{{" - "}} - {%- if doc.content is string -%} - {{doc.content}}{{" - "}} - {%- else -%} - {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{"---"}} - {%- endfor -%} - {%- endif -%} - summary: - type: string - nullable: true - description: Summary (null at the beginning) - generated automatically after every interaction - default: null - readOnly: true - render_templates: - type: boolean - description: Render system and assistant message content as jinja templates - default: true - token_budget: - type: integer - format: uint16 - nullable: true - description: Threshold value for the adaptive context functionality - default: null - context_overflow: - oneOf: - - $ref: '#/components/schemas/Sessions.ContextOverflowType' - nullable: true - description: Action to start on context window overflow - default: null - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be sent back to the model as the model's input. - If a tool call is not made, the model's output will be returned as is. - default: null - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - metadata: - type: object - additionalProperties: {} - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - kind: - type: string - description: Discriminator property for Session. - discriminator: - propertyName: kind - mapping: - single_agent_no_user: '#/components/schemas/Sessions.SingleAgentNoUserSession' - single_agent_single_user: '#/components/schemas/Sessions.SingleAgentSingleUserSession' - single_agent_multi_user: '#/components/schemas/Sessions.SingleAgentMultiUserSession' - multi_agent_no_user: '#/components/schemas/Sessions.MultiAgentNoUserSession' - multi_agent_single_user: '#/components/schemas/Sessions.MultiAgentSingleUserSession' - multi_agent_multi_user: '#/components/schemas/Sessions.MultiAgentMultiUserSession' - Sessions.SingleAgentMultiUserSession: - type: object - required: - - agent - - users - properties: - agent: - $ref: '#/components/schemas/Common.uuid' - users: - type: array - items: - $ref: '#/components/schemas/Common.uuid' - minItems: 2 - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.SingleAgentNoUserSession: - type: object - required: - - agent - properties: - agent: - $ref: '#/components/schemas/Common.uuid' - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.SingleAgentSingleUserSession: - type: object - required: - - agent - - user - properties: - agent: - $ref: '#/components/schemas/Common.uuid' - user: - $ref: '#/components/schemas/Common.uuid' - allOf: - - $ref: '#/components/schemas/Sessions.Session' - Sessions.UpdateSessionRequest: - type: object - required: - - situation - - render_templates - - token_budget - - context_overflow - - forward_tool_results - properties: - situation: - type: string - description: A specific situation that sets the background for this session - default: |- - {%- if agent.name -%} - You are {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} - {%- endif -%} - - {%- if user -%} - You are talking to a user - {%- if user.name -%}{{" "}} and their name is {{user.name}} - {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} - {%- endif -%} - {%- endif -%} - - {{" - - "}} - - {%- if agent.instructions -%} - Instructions:{{" - "}} - {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} - {%- else -%} - {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{" - "}} - {%- endif -%} - - {%- if tools -%} - Tools:{{" - "}} - {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} - {%- endfor -%} - {{" - - "}} - {%- endif -%} - - {%- if docs -%} - Relevant documents:{{" - "}} - {%- for doc in docs -%} - {{doc.title}}{{" - "}} - {%- if doc.content is string -%} - {{doc.content}}{{" - "}} - {%- else -%} - {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} - {%- endfor -%} - {%- endif -%} - {{"---"}} - {%- endfor -%} - {%- endif -%} - render_templates: - type: boolean - description: Render system and assistant message content as jinja templates - default: true - token_budget: - type: integer - format: uint16 - nullable: true - description: Threshold value for the adaptive context functionality - default: null - context_overflow: - oneOf: - - $ref: '#/components/schemas/Sessions.ContextOverflowType' - nullable: true - description: Action to start on context window overflow - default: null - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be sent back to the model as the model's input. - If a tool call is not made, the model's output will be returned as is. - default: null - metadata: - type: object - additionalProperties: {} - description: Payload for updating a session - Tasks.CaseThen: - type: object - required: - - case - - then - properties: - case: - anyOf: - - $ref: '#/components/schemas/Common.PyExpression' - - type: string - enum: - - _ - description: The condition to evaluate - then: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - description: The steps to run if the condition is true - Tasks.CaseThenUpdateItem: - type: object - required: - - case - - then - properties: - case: - anyOf: - - $ref: '#/components/schemas/Common.PyExpression' - - type: string - enum: - - _ - description: The condition to evaluate - then: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - description: The steps to run if the condition is true - Tasks.CreateTaskRequest: - type: object - required: - - name - - description - - main - - input_schema - - tools - - inherit_tools - properties: - name: - type: string - description: - type: string - default: '' - main: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - minItems: 1 - description: The entrypoint of the task. - input_schema: - type: object - additionalProperties: {} - nullable: true - description: The schema for the input to the task. `null` means all inputs are valid. - default: null - tools: - type: array - items: - $ref: '#/components/schemas/Tasks.TaskTool' - description: Tools defined specifically for this task not included in the Agent itself. - default: [] - inherit_tools: - type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true - metadata: - type: object - additionalProperties: {} - additionalProperties: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - description: Payload for creating a task - Tasks.ErrorWorkflowStep: - type: object - required: - - kind_ - - error - properties: - kind_: - type: string - enum: - - error - default: error - readOnly: true - error: - type: string - description: The error message - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - error - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.EvaluateStep: - type: object - required: - - kind_ - - evaluate - properties: - kind_: - type: string - enum: - - evaluate - default: evaluate - readOnly: true - evaluate: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - description: The expression to evaluate - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - evaluate - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ForeachDo: - type: object - required: - - in - - do - properties: - in: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The variable to iterate over. - VALIDATION: Should NOT return more than 1000 elements. - do: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - Tasks.ForeachDoUpdateItem: - type: object - required: - - in - - do - properties: - in: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The variable to iterate over. - VALIDATION: Should NOT return more than 1000 elements. - do: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - Tasks.ForeachStep: - type: object - required: - - kind_ - - foreach - properties: - kind_: - type: string - enum: - - foreach - default: foreach - readOnly: true - foreach: - allOf: - - $ref: '#/components/schemas/Tasks.ForeachDo' - description: The steps to run for each iteration - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - foreach - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ForeachStepUpdateItem: - type: object - required: - - foreach - properties: - foreach: - allOf: - - $ref: '#/components/schemas/Tasks.ForeachDoUpdateItem' - description: The steps to run for each iteration - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - mapping: {} - Tasks.GetStep: - type: object - required: - - kind_ - - get - properties: - kind_: - type: string - enum: - - get - default: get - readOnly: true - get: - type: string - description: The key to get - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - get - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.IfElseWorkflowStep: - type: object - required: - - kind_ - - if - - then - - else - properties: - kind_: - type: string - enum: - - if_else - default: if_else - readOnly: true - if: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The condition to evaluate - then: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - description: The steps to run if the condition is true - else: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - nullable: true - description: The steps to run if the condition is false - default: null - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - if_else - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.IfElseWorkflowStepUpdateItem: - type: object - required: - - if - - then - - else - properties: - if: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The condition to evaluate - then: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - description: The steps to run if the condition is true - else: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - nullable: true - description: The steps to run if the condition is false - default: null - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - mapping: {} - Tasks.LogStep: - type: object - required: - - kind_ - - log - properties: - kind_: - type: string - enum: - - log - default: log - readOnly: true - log: - allOf: - - $ref: '#/components/schemas/Common.JinjaTemplate' - description: The value to log - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - log - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ParallelStep: - type: object - required: - - kind_ - - parallel - properties: - kind_: - type: string - enum: - - parallel - default: parallel - readOnly: true - parallel: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - maxItems: 100 - description: The steps to run in parallel. Max concurrency will depend on the platform. - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - parallel - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ParallelStepUpdateItem: - type: object - required: - - parallel - properties: - parallel: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - maxItems: 100 - description: The steps to run in parallel. Max concurrency will depend on the platform. - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - mapping: {} - Tasks.PatchTaskRequest: - type: object - properties: - description: - type: string - default: '' - main: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStepUpdateItem' - - $ref: '#/components/schemas/Tasks.SwitchStepUpdateItem' - - $ref: '#/components/schemas/Tasks.ForeachStepUpdateItem' - - $ref: '#/components/schemas/Tasks.ParallelStepUpdateItem' - - type: object - required: - - over - - map - properties: - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - minItems: 1 - description: The entrypoint of the task. - input_schema: - type: object - additionalProperties: {} - nullable: true - description: The schema for the input to the task. `null` means all inputs are valid. - default: null - tools: - type: array - items: - $ref: '#/components/schemas/Tasks.TaskTool' - description: Tools defined specifically for this task not included in the Agent itself. - default: [] - inherit_tools: - type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true - metadata: - type: object - additionalProperties: {} - additionalProperties: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStepUpdateItem' - - $ref: '#/components/schemas/Tasks.SwitchStepUpdateItem' - - $ref: '#/components/schemas/Tasks.ForeachStepUpdateItem' - - $ref: '#/components/schemas/Tasks.ParallelStepUpdateItem' - - type: object - required: - - over - - map - properties: - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - description: Payload for patching a task - Tasks.PromptStep: - type: object - required: - - kind_ - - prompt - - tools - - forward_tool_results - properties: - kind_: - type: string - enum: - - prompt - default: prompt - readOnly: true - prompt: - anyOf: - - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - $ref: '#/components/schemas/Common.JinjaTemplate' - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - continue: - type: boolean - description: Whether to continue this message or return a new one - description: The prompt to run - tools: - anyOf: - - type: string - enum: - - all - - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.ToolRef' - - $ref: '#/components/schemas/Tools.CreateToolRequest' - description: The tools to use for the prompt - default: [] - tool_choice: - anyOf: - - type: string - enum: - - auto - - none - - $ref: '#/components/schemas/Tools.NamedToolChoice' - description: The tool choice for the prompt - settings: - allOf: - - $ref: '#/components/schemas/Chat.ChatSettings' - description: Settings for the prompt - unwrap: - type: boolean - description: Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` - default: false - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be used as the model's input. - If a tool call is not made, the model's output will be used as the next step's input. - default: null - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - prompt - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.PromptStepUpdateItem: - type: object - required: - - prompt - - tools - - forward_tool_results - properties: - prompt: - anyOf: - - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - type: object - required: - - role - - content - properties: - role: - allOf: - - $ref: '#/components/schemas/Entries.ChatMLRole' - description: The role of the message - content: - anyOf: - - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - $ref: '#/components/schemas/Common.JinjaTemplate' - - type: array - items: - anyOf: - - type: object - required: - - text - - type - properties: - text: - $ref: '#/components/schemas/Common.JinjaTemplate' - type: - type: string - enum: - - text - description: The type (fixed to 'text') - default: text - - type: object - required: - - image_url - - type - properties: - image_url: - type: object - required: - - url - - detail - properties: - url: - type: string - description: Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - detail: - allOf: - - $ref: '#/components/schemas/Entries.ImageDetail' - description: The detail level of the image - default: auto - description: The image URL - type: - type: string - enum: - - image_url - description: The type (fixed to 'image_url') - default: image_url - description: The content parts of the message - name: - type: string - description: Name - continue: - type: boolean - description: Whether to continue this message or return a new one - description: The prompt to run - tools: - anyOf: - - type: string - enum: - - all - - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.ToolRefUpdateItem' - - $ref: '#/components/schemas/Tools.CreateToolRequest' - description: The tools to use for the prompt - default: [] - tool_choice: - anyOf: - - type: string - enum: - - auto - - none - - $ref: '#/components/schemas/Tools.NamedToolChoice' - description: The tool choice for the prompt - settings: - allOf: - - $ref: '#/components/schemas/Chat.ChatSettings' - description: Settings for the prompt - unwrap: - type: boolean - description: Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` - default: false - forward_tool_results: - type: boolean - nullable: true - description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) - - If a tool call is made, the tool's output will be used as the model's input. - If a tool call is not made, the model's output will be used as the next step's input. - default: null - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ReturnStep: - type: object - required: - - kind_ - - return - properties: - kind_: - type: string - enum: - - return - default: return - readOnly: true - return: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - description: The value to return - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - return - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.SetStep: - type: object - required: - - kind_ - - set - properties: - kind_: - type: string - enum: - - set - default: set - readOnly: true - set: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - description: The value to set - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - set - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.SleepFor: - type: object - required: - - seconds - - minutes - - hours - - days - properties: - seconds: - type: integer - format: uint16 - minimum: 0 - maximum: 60 - description: The number of seconds to sleep for - default: 0 - minutes: - type: integer - format: uint16 - minimum: 0 - maximum: 60 - description: The number of minutes to sleep for - default: 0 - hours: - type: integer - format: uint16 - minimum: 0 - maximum: 24 - description: The number of hours to sleep for - default: 0 - days: - type: integer - format: uint16 - minimum: 0 - maximum: 30 - description: The number of days to sleep for - default: 0 - Tasks.SleepStep: - type: object - required: - - kind_ - - sleep - properties: - kind_: - type: string - enum: - - sleep - default: sleep - readOnly: true - sleep: - allOf: - - $ref: '#/components/schemas/Tasks.SleepFor' - description: The duration to sleep for (max 31 days) - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - sleep - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.SwitchStep: - type: object - required: - - kind_ - - switch - properties: - kind_: - type: string - enum: - - switch - default: switch - readOnly: true - switch: - type: array - items: - $ref: '#/components/schemas/Tasks.CaseThen' - minItems: 1 - description: The cond tree - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - switch - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.SwitchStepUpdateItem: - type: object - required: - - switch - properties: - switch: - type: array - items: - $ref: '#/components/schemas/Tasks.CaseThenUpdateItem' - minItems: 1 - description: The cond tree - allOf: - - type: object - properties: - kind_: - type: string - description: Discriminator property for BaseWorkflowStep. - discriminator: - propertyName: kind_ - mapping: {} - Tasks.Task: - type: object - required: - - name - - description - - main - - input_schema - - tools - - inherit_tools - - id - - created_at - - updated_at - properties: - name: - type: string - description: - type: string - default: '' - main: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - minItems: 1 - description: The entrypoint of the task. - input_schema: - type: object - additionalProperties: {} - nullable: true - description: The schema for the input to the task. `null` means all inputs are valid. - default: null - tools: - type: array - items: - $ref: '#/components/schemas/Tasks.TaskTool' - description: Tools defined specifically for this task not included in the Agent itself. - default: [] - inherit_tools: - type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - metadata: - type: object - additionalProperties: {} - additionalProperties: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - description: Object describing a Task - Tasks.TaskTool: - type: object - properties: - inherited: - type: boolean - description: 'Read-only: Whether the tool was inherited or not. Only applies within tasks.' - default: false - readOnly: true - allOf: - - $ref: '#/components/schemas/Tools.CreateToolRequest' - Tasks.ToolCallStep: - type: object - required: - - kind_ - - tool - - arguments - properties: - kind_: - type: string - enum: - - tool_call - default: tool_call - readOnly: true - tool: - allOf: - - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: The tool to run - arguments: - anyOf: - - type: object - additionalProperties: - anyOf: - - $ref: '#/components/schemas/Common.PyExpression' - - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - - type: string - enum: - - _ - description: The input parameters for the tool (defaults to last step output) - default: _ - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - tool_call - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.ToolRef: - type: object - required: - - ref - properties: - ref: - anyOf: - - $ref: '#/components/schemas/Tasks.ToolRefById' - - $ref: '#/components/schemas/Tasks.ToolRefByName' - description: Reference to a tool - Tasks.ToolRefById: - type: object - properties: - id: - $ref: '#/components/schemas/Common.uuid' - description: Reference to a tool by id - Tasks.ToolRefByName: - type: object - properties: - name: - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: Reference to a tool by name - Tasks.ToolRefUpdateItem: - type: object - description: Reference to a tool - Tasks.UpdateTaskRequest: - type: object - required: - - description - - main - - input_schema - - tools - - inherit_tools - properties: - description: - type: string - default: '' - main: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - minItems: 1 - description: The entrypoint of the task. - input_schema: - type: object - additionalProperties: {} - nullable: true - description: The schema for the input to the task. `null` means all inputs are valid. - default: null - tools: - type: array - items: - $ref: '#/components/schemas/Tasks.TaskTool' - description: Tools defined specifically for this task not included in the Agent itself. - default: [] - inherit_tools: - type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true - metadata: - type: object - additionalProperties: {} - additionalProperties: - type: array - items: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.ReturnStep' - - $ref: '#/components/schemas/Tasks.SleepStep' - - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - - $ref: '#/components/schemas/Tasks.WaitForInputStep' - - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - - $ref: '#/components/schemas/Tasks.SwitchStep' - - $ref: '#/components/schemas/Tasks.ForeachStep' - - $ref: '#/components/schemas/Tasks.ParallelStep' - - type: object - required: - - kind_ - - over - - map - properties: - kind_: - type: string - enum: - - map_reduce - default: map_reduce - readOnly: true - over: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: The variable to iterate over - map: - anyOf: - - $ref: '#/components/schemas/Tasks.EvaluateStep' - - $ref: '#/components/schemas/Tasks.ToolCallStep' - - $ref: '#/components/schemas/Tasks.PromptStep' - - $ref: '#/components/schemas/Tasks.GetStep' - - $ref: '#/components/schemas/Tasks.SetStep' - - $ref: '#/components/schemas/Tasks.LogStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - description: The steps to run for each iteration - reduce: - allOf: - - $ref: '#/components/schemas/Common.PyExpression' - description: |- - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - initial: - description: The initial value of the reduce expression - default: [] - parallelism: - type: integer - format: uint16 - minimum: 1 - maximum: 100 - description: Whether to run the reduce expression in parallel and how many items to run in each batch - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - map_reduce - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - description: Payload for updating a task - Tasks.WaitForInputInfo: - type: object - required: - - info - properties: - info: - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - description: Any additional info or data - Tasks.WaitForInputStep: - type: object - required: - - kind_ - - wait_for_input - properties: - kind_: - type: string - enum: - - wait_for_input - default: wait_for_input - readOnly: true - wait_for_input: - allOf: - - $ref: '#/components/schemas/Tasks.WaitForInputInfo' - description: Any additional info or data - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - wait_for_input - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tasks.YieldStep: - type: object - required: - - kind_ - - workflow - - arguments - properties: - kind_: - type: string - enum: - - yield - default: yield - readOnly: true - workflow: - type: string - description: |- - The subworkflow to run. - VALIDATION: Should resolve to a defined subworkflow. - arguments: - anyOf: - - type: object - additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' - - type: string - enum: - - _ - description: The input parameters for the subworkflow (defaults to last step output) - default: _ - allOf: - - type: object - required: - - kind_ - properties: - kind_: - type: string - enum: - - yield - description: The kind of step - readOnly: true - discriminator: - propertyName: kind_ - mapping: {} - Tools.ApiCallDef: - type: object - required: - - method - - url - properties: - method: - type: string - enum: - - GET - - POST - - PUT - - DELETE - - PATCH - - HEAD - - OPTIONS - - CONNECT - - TRACE - description: The HTTP method to use - url: - type: string - format: uri - description: The URL to call - headers: - type: object - additionalProperties: - type: string - description: The headers to send with the request - content: - type: string - description: The content as base64 to send with the request - data: - type: object - additionalProperties: {} - description: The data to send as form data - json: - type: object - additionalProperties: {} - description: JSON body to send with the request - cookies: - type: object - additionalProperties: - type: string - description: Cookies - params: - anyOf: - - type: string - - type: object - additionalProperties: {} - description: The parameters to send with the request - follow_redirects: - type: boolean - description: Follow redirects - timeout: - type: integer - format: uint8 - description: The timeout for the request - description: API call definition - Tools.ApiCallDefUpdate: - type: object - properties: - method: - type: string - enum: - - GET - - POST - - PUT - - DELETE - - PATCH - - HEAD - - OPTIONS - - CONNECT - - TRACE - description: The HTTP method to use - url: - type: string - format: uri - description: The URL to call - headers: - type: object - additionalProperties: - type: string - description: The headers to send with the request - content: - type: string - description: The content as base64 to send with the request - data: - type: object - additionalProperties: {} - description: The data to send as form data - json: - type: object - additionalProperties: {} - description: JSON body to send with the request - cookies: - type: object - additionalProperties: - type: string - description: Cookies - params: - anyOf: - - type: string - - type: object - additionalProperties: {} - description: The parameters to send with the request - follow_redirects: - type: boolean - description: Follow redirects - timeout: - type: integer - format: uint8 - description: The timeout for the request - description: API call definition - Tools.ChosenFunctionCall: - type: object - required: - - type - - function - properties: - type: - type: string - enum: - - function - function: - allOf: - - $ref: '#/components/schemas/Tools.FunctionCallOption' - description: The function to call - allOf: - - $ref: '#/components/schemas/Tools.ChosenToolCall' - Tools.ChosenToolCall: - type: object - required: - - type - - id - properties: - type: - allOf: - - $ref: '#/components/schemas/Tools.ToolType' - description: Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - function: - $ref: '#/components/schemas/Tools.FunctionCallOption' - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - discriminator: - propertyName: type - mapping: - function: '#/components/schemas/Tools.ChosenFunctionCall' - description: The response tool value generated by the model - Tools.CreateToolRequest: - type: object - required: - - name - properties: - name: - allOf: - - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: Name of the tool (must be unique for this agent and a valid python identifier string ) - description: - type: string - description: Description of the tool - function: - allOf: - - $ref: '#/components/schemas/Tools.FunctionDef' - description: The function to call - integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' - description: The integration to call - system: - allOf: - - $ref: '#/components/schemas/Tools.SystemDef' - description: The system to call - api_call: - allOf: - - $ref: '#/components/schemas/Tools.ApiCallDef' - description: The API call to make - description: Payload for creating a tool - Tools.FunctionCallOption: - type: object - required: - - name - properties: - name: - type: string - description: The name of the function - Tools.FunctionDef: - type: object - properties: - name: - nullable: true - description: 'DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons.' - default: null - description: - nullable: true - description: 'DO NOT USE: This will be overriden by the tool description. Here only for compatibility reasons.' - default: null - parameters: - type: object - additionalProperties: {} - description: The parameters the function accepts - description: Function definition - Tools.IntegrationDef: - type: object - required: - - provider - properties: - provider: - anyOf: - - type: string - enum: - - dummy - - hacker_news - - weather - - wikipedia - - spider - - brave - - browserbase - - email - - type: string - description: The provider of the integration - method: - type: string - description: The specific method of the integration to call - setup: - type: object - additionalProperties: {} - description: The setup parameters the integration accepts - arguments: - type: object - additionalProperties: {} - description: The arguments to pre-apply to the integration call - description: Integration definition - Tools.IntegrationDefUpdate: - type: object - properties: - provider: - anyOf: - - type: string - enum: - - dummy - - hacker_news - - weather - - wikipedia - - spider - - brave - - browserbase - - email - - type: string - description: The provider of the integration - method: - type: string - description: The specific method of the integration to call - setup: - type: object - additionalProperties: {} - description: The setup parameters the integration accepts - arguments: - type: object - additionalProperties: {} - description: The arguments to pre-apply to the integration call - description: Integration definition - Tools.NamedToolChoice: - type: object - properties: - function: - $ref: '#/components/schemas/Tools.FunctionCallOption' - Tools.PatchToolRequest: - type: object - properties: - name: - allOf: - - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: Name of the tool (must be unique for this agent and a valid python identifier string ) - description: - type: string - description: Description of the tool - function: - allOf: - - $ref: '#/components/schemas/Tools.FunctionDef' - description: The function to call - integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDefUpdate' - description: The integration to call - system: - allOf: - - $ref: '#/components/schemas/Tools.SystemDefUpdate' - description: The system to call - api_call: - allOf: - - $ref: '#/components/schemas/Tools.ApiCallDefUpdate' - description: The API call to make - description: Payload for patching a tool - Tools.SystemDef: - type: object - required: - - resource - - operation - properties: - resource: - type: string - enum: - - agent - - user - - task - - execution - - doc - - session - - job - description: Resource is the name of the resource to use - operation: - type: string - enum: - - create - - update - - patch - - create_or_update - - embed - - change_status - - search - - chat - - history - - delete - - get - - list - description: Operation is the name of the operation to perform - resource_id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Resource id (if applicable) - subresource: - type: string - enum: - - tool - - doc - - execution - - transition - description: Sub-resource type (if applicable) - arguments: - type: object - additionalProperties: {} - description: The arguments to pre-apply to the system call - description: System definition - Tools.SystemDefUpdate: - type: object - properties: - resource: - type: string - enum: - - agent - - user - - task - - execution - - doc - - session - - job - description: Resource is the name of the resource to use - operation: - type: string - enum: - - create - - update - - patch - - create_or_update - - embed - - change_status - - search - - chat - - history - - delete - - get - - list - description: Operation is the name of the operation to perform - resource_id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - description: Resource id (if applicable) - subresource: - type: string - enum: - - tool - - doc - - execution - - transition - description: Sub-resource type (if applicable) - arguments: - type: object - additionalProperties: {} - description: The arguments to pre-apply to the system call - description: System definition - Tools.Tool: - type: object - required: - - name - - created_at - - updated_at - - id - properties: - name: - allOf: - - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: Name of the tool (must be unique for this agent and a valid python identifier string ) - description: - type: string - description: Description of the tool - function: - allOf: - - $ref: '#/components/schemas/Tools.FunctionDef' - description: The function to call - integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' - description: The integration to call - system: - allOf: - - $ref: '#/components/schemas/Tools.SystemDef' - description: The system to call - api_call: - allOf: - - $ref: '#/components/schemas/Tools.ApiCallDef' - description: The API call to make - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - Tools.ToolResponse: - type: object - required: - - id - - output - properties: - id: - $ref: '#/components/schemas/Common.uuid' - output: - type: object - additionalProperties: {} - description: The output of the tool - Tools.ToolType: - type: string - enum: - - function - - integration - - system - - api_call - Tools.UpdateToolRequest: - type: object - required: - - name - properties: - name: - allOf: - - $ref: '#/components/schemas/Common.validPythonIdentifier' - description: Name of the tool (must be unique for this agent and a valid python identifier string ) - description: - type: string - description: Description of the tool - function: - allOf: - - $ref: '#/components/schemas/Tools.FunctionDef' - description: The function to call - integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' - description: The integration to call - system: - allOf: - - $ref: '#/components/schemas/Tools.SystemDef' - description: The system to call - api_call: - allOf: - - $ref: '#/components/schemas/Tools.ApiCallDef' - description: The API call to make - description: Payload for updating a tool - Users.CreateOrUpdateUserRequest: - type: object - required: - - id - properties: - id: - $ref: '#/components/schemas/Common.uuid' - allOf: - - $ref: '#/components/schemas/Users.CreateUserRequest' - Users.CreateUserRequest: - type: object - required: - - name - - about - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the user - default: '' - about: - type: string - description: About the user - default: '' - description: Payload for creating a user (and associated documents) - Users.PatchUserRequest: - type: object - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the user - default: '' - about: - type: string - description: About the user - default: '' - description: Payload for patching a user - Users.UpdateUserRequest: - type: object - required: - - name - - about - properties: - metadata: - type: object - additionalProperties: {} - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the user - default: '' - about: - type: string - description: About the user - default: '' - description: Payload for updating a user - Users.User: - type: object - required: - - id - - created_at - - updated_at - - name - - about - properties: - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - metadata: - type: object - additionalProperties: {} - created_at: - type: string - format: date-time - description: When this resource was created as UTC date-time - readOnly: true - updated_at: - type: string - format: date-time - description: When this resource was updated as UTC date-time - readOnly: true - name: - allOf: - - $ref: '#/components/schemas/Common.identifierSafeUnicode' - description: Name of the user - default: '' - about: - type: string - description: About the user - default: '' - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: Authorization - ApiKeyAuth_: - type: apiKey - in: header - name: X-Auth-Key -servers: - - url: https://{serverEnv}.julep.ai/api - description: The julep cloud service endpoint - variables: - serverEnv: - default: api-alpha - description: The environment to use - enum: - - api - - api-alpha diff --git a/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml b/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml index b3f3919a9..966b66055 100644 --- a/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml +++ b/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml @@ -617,7 +617,9 @@ paths: type: object properties: body: - $ref: '#/components/schemas/Docs.EmbedQueryRequest' + anyOf: + - $ref: '#/components/schemas/Docs.SingleEmbedQueryRequest' + - $ref: '#/components/schemas/Docs.MultipleEmbedQueryRequest' required: - body /executions: @@ -752,7 +754,43 @@ paths: content: text/event-stream: schema: - $ref: '#/components/schemas/Executions.TransitionEvent' + type: string + /files: + post: + operationId: FilesRoute_create + description: Create a new File + parameters: [] + responses: + '201': + description: The request has succeeded and a new resource has been created as a result. + content: + application/json: + schema: + $ref: '#/components/schemas/Common.ResourceCreatedResponse' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Files.CreateFileRequest' + /files/{id}: + get: + operationId: FilesRoute_get + description: Get a File by its id + parameters: + - name: id + in: path + required: true + description: ID of the resource + schema: + $ref: '#/components/schemas/Common.uuid' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Files.File' /jobs/{id}: get: operationId: JobRoute_get @@ -979,6 +1017,24 @@ paths: application/json: schema: $ref: '#/components/schemas/Entries.History' + /tasks/{id}: + get: + operationId: TasksGetRoute_get + description: Get a task by id + parameters: + - name: id + in: path + required: true + description: ID of the resource + schema: + $ref: '#/components/schemas/Common.uuid' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Tasks.Task' /tasks/{id}/executions: post: operationId: TaskExecutionsRoute_create @@ -1320,12 +1376,8 @@ components: description: Object to filter results by metadata schema: type: object - additionalProperties: - anyOf: - - type: number - - type: string - - type: boolean - nullable: true + additionalProperties: {} + default: {} explode: false Common.PaginationOptions.offset: name: offset @@ -1585,6 +1637,15 @@ components: allOf: - $ref: '#/components/schemas/Chat.LogProbResponse' description: The log probabilities of tokens + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + description: The tool calls generated by the model Chat.BaseChatResponse: type: object required: @@ -1761,6 +1822,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - type: string @@ -1809,6 +1872,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -1816,12 +1939,23 @@ components: continue: type: boolean description: Whether to continue this message or return a new one + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + nullable: true + description: Tool calls generated by the model. + default: [] minItems: 1 description: A list of new input messages comprising the conversation so far. tools: type: array items: - $ref: '#/components/schemas/Tools.Tool' + $ref: '#/components/schemas/Tools.CreateToolRequest' description: (Advanced) List of tools that are provided in addition to agent's default set of tools. default: [] tool_choice: @@ -1847,6 +1981,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - type: string @@ -1895,6 +2031,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -1902,6 +2098,17 @@ components: continue: type: boolean description: Whether to continue this message or return a new one + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + nullable: true + description: Tool calls generated by the model. + default: [] description: The message generated by the model allOf: - $ref: '#/components/schemas/Chat.BaseChatOutput' @@ -2064,6 +2271,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - type: string @@ -2112,6 +2321,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -2119,11 +2388,14 @@ components: tool_calls: type: array items: - $ref: '#/components/schemas/Tools.ChosenToolCall' + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' nullable: true description: Tool calls generated by the model. default: [] - readOnly: true created_at: type: string format: date-time @@ -2208,6 +2480,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - type: string @@ -2256,6 +2530,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -2263,11 +2597,14 @@ components: tool_calls: type: array items: - $ref: '#/components/schemas/Tools.ChosenToolCall' + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' nullable: true description: Tool calls generated by the model. default: [] - readOnly: true created_at: type: string format: date-time @@ -2389,6 +2726,11 @@ components: format: float minimum: -100 maximum: 100 + Common.mimeType: + type: string + maxLength: 120 + pattern: ^(application|audio|font|example|image|message|model|multipart|text|video|x-(?:[0-9A-Za-z!#$%&'*+.^_`|~-]+))\/([0-9A-Za-z!#$%&'*+.^_`|~-]+)$ + description: Valid mime types Common.offset: type: integer format: uint32 @@ -2407,12 +2749,13 @@ components: required: - limit - lang + - metadata_filter properties: limit: type: integer format: uint16 minimum: 1 - maximum: 100 + maximum: 50 default: 10 lang: type: string @@ -2420,11 +2763,23 @@ components: - en-US description: The language to be used for text-only search. Support for other languages coming soon. default: en-US + metadata_filter: + type: object + additionalProperties: {} + default: {} + mmr_strength: + type: number + minimum: 0 + maximum: 1 + exclusiveMaximum: true + description: MMR Strength (mmr_strength = 1 - mmr_lambda) + default: 0 Docs.CreateDocRequest: type: object required: - title - content + - embed_instruction properties: metadata: type: object @@ -2440,6 +2795,11 @@ components: items: type: string description: Contents of the document + embed_instruction: + type: string + nullable: true + description: Instruction for the embedding model. + default: null description: Payload for creating a doc Docs.Doc: type: object @@ -2510,9 +2870,12 @@ components: required: - owner - id - - snippets + - snippet - distance properties: + metadata: + type: object + additionalProperties: {} owner: allOf: - $ref: '#/components/schemas/Docs.DocOwner' @@ -2524,11 +2887,8 @@ components: description: ID of the document title: type: string - snippets: - type: array - items: - $ref: '#/components/schemas/Docs.Snippet' - minItems: 1 + snippet: + $ref: '#/components/schemas/Docs.Snippet' distance: type: number nullable: true @@ -2549,18 +2909,6 @@ components: minimum: 0 exclusiveMinimum: true description: The time taken to search in seconds - Docs.EmbedQueryRequest: - type: object - required: - - text - properties: - text: - anyOf: - - type: string - - type: array - items: - type: string - description: Text or texts to embed Docs.EmbedQueryResponse: type: object required: @@ -2603,6 +2951,36 @@ components: description: Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. allOf: - $ref: '#/components/schemas/Docs.BaseDocSearchRequest' + Docs.MultipleEmbedQueryRequest: + type: object + required: + - text + - embed_instruction + properties: + text: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + description: Texts to embed + embed_instruction: + type: string + description: Instruction for the embedding model. + default: '' + Docs.SingleEmbedQueryRequest: + type: object + required: + - text + - embed_instruction + properties: + text: + type: string + description: Text to embed + embed_instruction: + type: string + description: Instruction for the embedding model. + default: '' Docs.Snippet: type: object required: @@ -2614,6 +2992,10 @@ components: format: uint16 content: type: string + embedding: + type: array + items: + type: number Docs.TextOnlyDocSearchRequest: type: object required: @@ -2704,8 +3086,70 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part - $ref: '#/components/schemas/Tools.Tool' - - $ref: '#/components/schemas/Tools.ChosenToolCall' + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' - type: string - $ref: '#/components/schemas/Tools.ToolResponse' - type: array @@ -2753,8 +3197,70 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + type: string + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + type: string + description: Anthropic image content part - $ref: '#/components/schemas/Tools.Tool' - - $ref: '#/components/schemas/Tools.ChosenToolCall' + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' - type: string - $ref: '#/components/schemas/Tools.ToolResponse' source: @@ -2771,6 +3277,22 @@ components: token_count: type: integer format: uint16 + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + nullable: true + description: Tool calls generated by the model. + default: null + tool_call_id: + type: string + nullable: true + description: The tool call id of the tool call this message is a response to + default: null timestamp: type: number minimum: 0 @@ -2781,11 +3303,8 @@ components: - user - assistant - system - - function - - function_response - - function_call - - auto - description: ChatML role (system|assistant|user|function_call|function|function_response|auto) + - tool + description: ChatML role (system|assistant|user|tool) Entries.Entry: type: object required: @@ -3062,30 +3581,104 @@ components: mapping: cancelled: '#/components/schemas/Executions.StopExecutionRequest' running: '#/components/schemas/Executions.ResumeExecutionRequest' - Jobs.JobState: - type: string - enum: - - pending - - in_progress - - retrying - - succeeded - - aborted - - failed - - unknown - description: 'Current state (one of: pending, in_progress, retrying, succeeded, aborted, failed)' - Jobs.JobStatus: + Files.CreateFileRequest: type: object required: - - id - - created_at - - updated_at - name - - reason - - has_progress - - progress - - state + - description + - mime_type + - content properties: - id: + name: + allOf: + - $ref: '#/components/schemas/Common.identifierSafeUnicode' + description: Name of the file + description: + type: string + description: Description of the file + default: '' + mime_type: + oneOf: + - $ref: '#/components/schemas/Common.mimeType' + nullable: true + description: MIME type of the file + default: null + content: + type: string + description: Base64 encoded content of the file + description: Payload for creating a file + Files.File: + type: object + required: + - id + - created_at + - name + - description + - mime_type + - content + - size + - hash + properties: + id: + allOf: + - $ref: '#/components/schemas/Common.uuid' + readOnly: true + created_at: + type: string + format: date-time + description: When this resource was created as UTC date-time + readOnly: true + name: + allOf: + - $ref: '#/components/schemas/Common.identifierSafeUnicode' + description: Name of the file + description: + type: string + description: Description of the file + default: '' + mime_type: + oneOf: + - $ref: '#/components/schemas/Common.mimeType' + nullable: true + description: MIME type of the file + default: null + content: + type: string + description: Base64 encoded content of the file + size: + type: integer + format: uint64 + minimum: 1 + description: Size of the file in bytes + readOnly: true + hash: + type: string + description: Hash of the file + readOnly: true + Jobs.JobState: + type: string + enum: + - pending + - in_progress + - retrying + - succeeded + - aborted + - failed + - unknown + description: 'Current state (one of: pending, in_progress, retrying, succeeded, aborted, failed)' + Jobs.JobStatus: + type: object + required: + - id + - created_at + - updated_at + - name + - reason + - has_progress + - progress + - state + properties: + id: allOf: - $ref: '#/components/schemas/Common.uuid' readOnly: true @@ -3137,7 +3730,7 @@ components: - render_templates - token_budget - context_overflow - - forward_tool_results + - auto_run_tools properties: id: $ref: '#/components/schemas/Common.uuid' @@ -3166,7 +3759,7 @@ components: {%- endif -%} {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} + About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -3176,56 +3769,38 @@ components: {%- endif -%} {%- endif -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- if agent.instructions -%} - Instructions:{{" - "}} + Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{" - "}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} - Tools:{{" - "}} + Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} - Relevant documents:{{" - "}} + Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{" - "}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{" - "}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} @@ -3247,18 +3822,15 @@ components: nullable: true description: Action to start on context window overflow default: null - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. - default: null + default: false metadata: type: object additionalProperties: {} @@ -3271,7 +3843,7 @@ components: - render_templates - token_budget - context_overflow - - forward_tool_results + - auto_run_tools properties: user: allOf: @@ -3298,7 +3870,7 @@ components: {%- endif -%} {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} + About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -3308,56 +3880,38 @@ components: {%- endif -%} {%- endif -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- if agent.instructions -%} - Instructions:{{" - "}} + Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{" - "}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} - Tools:{{" - "}} + Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} - Relevant documents:{{" - "}} + Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{" - "}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{" - "}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} @@ -3379,18 +3933,15 @@ components: nullable: true description: Action to start on context window overflow default: null - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. - default: null + default: false metadata: type: object additionalProperties: {} @@ -3452,7 +4003,7 @@ components: {%- endif -%} {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} + About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -3462,56 +4013,38 @@ components: {%- endif -%} {%- endif -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- if agent.instructions -%} - Instructions:{{" - "}} + Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{" - "}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} - Tools:{{" - "}} + Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} - Relevant documents:{{" - "}} + Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{" - "}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{" - "}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} @@ -3533,18 +4066,15 @@ components: nullable: true description: Action to start on context window overflow default: null - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. - default: null + default: false metadata: type: object additionalProperties: {} @@ -3557,7 +4087,7 @@ components: - render_templates - token_budget - context_overflow - - forward_tool_results + - auto_run_tools - id - created_at - updated_at @@ -3571,7 +4101,7 @@ components: {%- endif -%} {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} + About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -3581,56 +4111,38 @@ components: {%- endif -%} {%- endif -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- if agent.instructions -%} - Instructions:{{" - "}} + Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{" - "}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} - Tools:{{" - "}} + Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} - Relevant documents:{{" - "}} + Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{" - "}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{" - "}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} @@ -3658,18 +4170,15 @@ components: nullable: true description: Action to start on context window overflow default: null - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. - default: null + default: false id: allOf: - $ref: '#/components/schemas/Common.uuid' @@ -3742,7 +4251,7 @@ components: - render_templates - token_budget - context_overflow - - forward_tool_results + - auto_run_tools properties: situation: type: string @@ -3753,7 +4262,7 @@ components: {%- endif -%} {%- if agent.about -%} - About you: {{agent.name}}.{{" "}} + About you: {{agent.about}}.{{" "}} {%- endif -%} {%- if user -%} @@ -3763,56 +4272,38 @@ components: {%- endif -%} {%- endif -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- if agent.instructions -%} - Instructions:{{" - "}} + Instructions:{{NEWLINE}} {%- if agent.instructions is string -%} - {{agent.instructions}}{{" - "}} + {{agent.instructions}}{{NEWLINE}} {%- else -%} {%- for instruction in agent.instructions -%} - - {{instruction}}{{" - "}} + - {{instruction}}{{NEWLINE}} {%- endfor -%} {%- endif -%} - {{" - "}} + {{NEWLINE}} {%- endif -%} {%- if tools -%} - Tools:{{" - "}} + Tools:{{NEWLINE}} {%- for tool in tools -%} - {%- if tool.type == "function" -%} - - {{tool.function.name}} - {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{" - "}} - {%- else -%} - - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} - {%- endif -%} + - {{tool.name + NEWLINE}} + {%- if tool.description -%}: {{tool.description + NEWLINE}}{%- endif -%} {%- endfor -%} - {{" - - "}} + {{NEWLINE+NEWLINE}} {%- endif -%} {%- if docs -%} - Relevant documents:{{" - "}} + Relevant documents:{{NEWLINE}} {%- for doc in docs -%} - {{doc.title}}{{" - "}} + {{doc.title}}{{NEWLINE}} {%- if doc.content is string -%} - {{doc.content}}{{" - "}} + {{doc.content}}{{NEWLINE}} {%- else -%} {%- for snippet in doc.content -%} - {{snippet}}{{" - "}} + {{snippet}}{{NEWLINE}} {%- endfor -%} {%- endif -%} {{"---"}} @@ -3834,18 +4325,15 @@ components: nullable: true description: Action to start on context window overflow default: null - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: false for sessions, true for tasks) If a tool call is made, the tool's output will be sent back to the model as the model's input. If a tool call is not made, the model's output will be returned as is. - default: null + default: false metadata: type: object additionalProperties: {} @@ -3875,7 +4363,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' description: The steps to run if the condition is true Tasks.CaseThenUpdateItem: @@ -3903,7 +4390,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' description: The steps to run if the condition is true Tasks.CreateTaskRequest: @@ -3935,7 +4421,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -3994,6 +4479,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ minItems: 1 @@ -4012,8 +4500,8 @@ components: default: [] inherit_tools: type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true + description: Whether to inherit tools from the parent agent or not. Defaults to false. + default: false metadata: type: object additionalProperties: {} @@ -4031,7 +4519,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -4090,6 +4577,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ description: Payload for creating a task @@ -4119,6 +4609,9 @@ components: - error description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4137,7 +4630,19 @@ components: evaluate: type: object additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' description: The expression to evaluate allOf: - type: object @@ -4150,6 +4655,9 @@ components: - evaluate description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4167,6 +4675,7 @@ components: VALIDATION: Should NOT return more than 1000 elements. do: anyOf: + - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.EvaluateStep' - $ref: '#/components/schemas/Tasks.ToolCallStep' - $ref: '#/components/schemas/Tasks.PromptStep' @@ -4189,6 +4698,7 @@ components: VALIDATION: Should NOT return more than 1000 elements. do: anyOf: + - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.EvaluateStep' - $ref: '#/components/schemas/Tasks.ToolCallStep' - $ref: '#/components/schemas/Tasks.PromptStepUpdateItem' @@ -4224,6 +4734,9 @@ components: - foreach description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4239,6 +4752,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4271,6 +4787,9 @@ components: - get description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4304,7 +4823,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' description: The steps to run if the condition is true else: @@ -4319,7 +4837,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' nullable: true description: The steps to run if the condition is false @@ -4335,6 +4852,9 @@ components: - if_else description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4361,7 +4881,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' description: The steps to run if the condition is true else: @@ -4376,7 +4895,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' nullable: true description: The steps to run if the condition is false @@ -4384,6 +4902,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4417,6 +4938,9 @@ components: - log description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4456,6 +4980,9 @@ components: - parallel description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4480,6 +5007,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4506,7 +5036,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStepUpdateItem' - $ref: '#/components/schemas/Tasks.SwitchStepUpdateItem' @@ -4550,6 +5079,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4571,8 +5103,8 @@ components: default: [] inherit_tools: type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true + description: Whether to inherit tools from the parent agent or not. Defaults to false. + default: false metadata: type: object additionalProperties: {} @@ -4590,7 +5122,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStepUpdateItem' - $ref: '#/components/schemas/Tasks.SwitchStepUpdateItem' @@ -4634,6 +5165,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4646,7 +5180,8 @@ components: - kind_ - prompt - tools - - forward_tool_results + - auto_run_tools + - disable_cache properties: kind_: type: string @@ -4668,6 +5203,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - $ref: '#/components/schemas/Common.JinjaTemplate' @@ -4716,6 +5253,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + $ref: '#/components/schemas/Common.JinjaTemplate' + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + $ref: '#/components/schemas/Common.JinjaTemplate' + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -4723,6 +5320,17 @@ components: continue: type: boolean description: Whether to continue this message or return a new one + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + nullable: true + description: Tool calls generated by the model. + default: [] description: The prompt to run tools: anyOf: @@ -4735,7 +5343,7 @@ components: - $ref: '#/components/schemas/Tasks.ToolRef' - $ref: '#/components/schemas/Tools.CreateToolRequest' description: The tools to use for the prompt - default: [] + default: all tool_choice: anyOf: - type: string @@ -4752,18 +5360,19 @@ components: type: boolean description: Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` default: false - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) If a tool call is made, the tool's output will be used as the model's input. If a tool call is not made, the model's output will be used as the next step's input. - default: null + default: true + disable_cache: + type: boolean + description: Whether to disable caching for the prompt step + default: false allOf: - type: object required: @@ -4775,6 +5384,9 @@ components: - prompt description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4783,7 +5395,8 @@ components: required: - prompt - tools - - forward_tool_results + - auto_run_tools + - disable_cache properties: prompt: anyOf: @@ -4799,6 +5412,8 @@ components: allOf: - $ref: '#/components/schemas/Entries.ChatMLRole' description: The role of the message + tool_call_id: + type: string content: anyOf: - $ref: '#/components/schemas/Common.JinjaTemplate' @@ -4847,6 +5462,66 @@ components: - image_url description: The type (fixed to 'image_url') default: image_url + - type: object + required: + - tool_use_id + - type + - content + properties: + tool_use_id: + type: string + type: + type: string + enum: + - tool_result + default: tool_result + content: + anyOf: + - type: array + items: + type: object + required: + - text + - type + properties: + text: + $ref: '#/components/schemas/Common.JinjaTemplate' + type: + type: string + enum: + - text + description: The type (fixed to 'text') + default: text + - type: array + items: + type: object + required: + - type + - source + properties: + type: + type: string + enum: + - image + default: image + source: + type: object + required: + - type + - media_type + - data + properties: + type: + type: string + enum: + - base64 + default: base64 + media_type: + type: string + data: + $ref: '#/components/schemas/Common.JinjaTemplate' + description: Anthropic image content part + nullable: true description: The content parts of the message name: type: string @@ -4854,6 +5529,17 @@ components: continue: type: boolean description: Whether to continue this message or return a new one + tool_calls: + type: array + items: + anyOf: + - $ref: '#/components/schemas/Tools.ChosenFunctionCall' + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + - $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + - $ref: '#/components/schemas/Tools.ChosenBash20241022' + nullable: true + description: Tool calls generated by the model. + default: [] description: The prompt to run tools: anyOf: @@ -4866,7 +5552,7 @@ components: - $ref: '#/components/schemas/Tasks.ToolRefUpdateItem' - $ref: '#/components/schemas/Tools.CreateToolRequest' description: The tools to use for the prompt - default: [] + default: all tool_choice: anyOf: - type: string @@ -4883,21 +5569,25 @@ components: type: boolean description: Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content` default: false - forward_tool_results: + auto_run_tools: type: boolean - nullable: true description: |- - Whether to forward the tool results to the model when available. - "true" => always forward - "false" => never forward - null => forward if applicable (default) + Whether to auto-run the tool and send the tool results to the model when available. + (default: true for prompt steps, false for sessions) If a tool call is made, the tool's output will be used as the model's input. If a tool call is not made, the model's output will be used as the next step's input. - default: null + default: true + disable_cache: + type: boolean + description: Whether to disable caching for the prompt step + default: false allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -4919,7 +5609,19 @@ components: return: type: object additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' description: The value to return allOf: - type: object @@ -4932,6 +5634,9 @@ components: - return description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -4963,6 +5668,9 @@ components: - set description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5029,6 +5737,9 @@ components: - sleep description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5061,6 +5772,9 @@ components: - switch description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5078,6 +5792,9 @@ components: allOf: - type: object properties: + label: + type: string + description: The label of this step for referencing it from other steps kind_: type: string description: Discriminator property for BaseWorkflowStep. @@ -5116,7 +5833,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -5175,6 +5891,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ minItems: 1 @@ -5193,8 +5912,8 @@ components: default: [] inherit_tools: type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true + description: Whether to inherit tools from the parent agent or not. Defaults to false. + default: false id: allOf: - $ref: '#/components/schemas/Common.uuid' @@ -5226,7 +5945,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -5285,6 +6003,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ description: Object describing a Task @@ -5323,7 +6044,74 @@ components: - $ref: '#/components/schemas/Common.PyExpression' - type: object additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' - type: string enum: - _ @@ -5340,6 +6128,9 @@ components: - tool_call description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5357,7 +6148,7 @@ components: type: object properties: id: - $ref: '#/components/schemas/Common.uuid' + type: string description: Reference to a tool by id Tasks.ToolRefByName: type: object @@ -5394,7 +6185,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -5453,6 +6243,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ minItems: 1 @@ -5471,8 +6264,8 @@ components: default: [] inherit_tools: type: boolean - description: Whether to inherit tools from the parent agent or not. Defaults to true. - default: true + description: Whether to inherit tools from the parent agent or not. Defaults to false. + default: false metadata: type: object additionalProperties: {} @@ -5490,7 +6283,6 @@ components: - $ref: '#/components/schemas/Tasks.ReturnStep' - $ref: '#/components/schemas/Tasks.SleepStep' - $ref: '#/components/schemas/Tasks.ErrorWorkflowStep' - - $ref: '#/components/schemas/Tasks.YieldStep' - $ref: '#/components/schemas/Tasks.WaitForInputStep' - $ref: '#/components/schemas/Tasks.IfElseWorkflowStep' - $ref: '#/components/schemas/Tasks.SwitchStep' @@ -5549,6 +6341,9 @@ components: - map_reduce description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ description: Payload for updating a task @@ -5560,7 +6355,19 @@ components: info: type: object additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' description: Any additional info or data Tasks.WaitForInputStep: type: object @@ -5589,6 +6396,9 @@ components: - wait_for_input description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5614,7 +6424,19 @@ components: anyOf: - type: object additionalProperties: - $ref: '#/components/schemas/Common.PyExpression' + anyOf: + - $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + $ref: '#/components/schemas/Common.PyExpression' + - type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' + - type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/Common.PyExpression' - type: string enum: - _ @@ -5631,6 +6453,9 @@ components: - yield description: The kind of step readOnly: true + label: + type: string + description: The label of this step for referencing it from other steps discriminator: propertyName: kind_ mapping: {} @@ -5657,6 +6482,10 @@ components: type: string format: uri description: The URL to call + schema: + type: object + additionalProperties: {} + description: The schema of the response headers: type: object additionalProperties: @@ -5669,6 +6498,10 @@ components: type: object additionalProperties: {} description: The data to send as form data + files: + type: object + additionalProperties: {} + description: The data to send as files data json: type: object additionalProperties: {} @@ -5712,6 +6545,10 @@ components: type: string format: uri description: The URL to call + schema: + type: object + additionalProperties: {} + description: The schema of the response headers: type: object additionalProperties: @@ -5724,6 +6561,10 @@ components: type: object additionalProperties: {} description: The data to send as form data + files: + type: object + additionalProperties: {} + description: The data to send as files data json: type: object additionalProperties: {} @@ -5747,52 +6588,1195 @@ components: format: uint8 description: The timeout for the request description: API call definition - Tools.ChosenFunctionCall: + Tools.ArxivIntegrationDef: type: object required: - - type - - function + - provider properties: - type: + provider: type: string enum: - - function + - arxiv + description: The provider must be "arxiv" + default: arxiv + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Arxiv + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.ArxivSearchArguments' + description: The arguments for Arxiv Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Arxiv integration definition + Tools.ArxivIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - arxiv + description: The provider must be "arxiv" + default: arxiv + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Arxiv + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.ArxivSearchArgumentsUpdate' + description: The arguments for Arxiv Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Arxiv integration definition + Tools.ArxivSearchArguments: + type: object + required: + - query + properties: + query: + type: string + description: The search query for searching with Arxiv + id_list: + type: array + items: + type: string + description: The list of Arxiv IDs to search with + max_results: + type: integer + format: int16 + minimum: 1 + maximum: 300000 + description: The maximum number of results to return + default: 5 + download_pdf: + type: boolean + description: The download the pdf of the results + default: false + sort_by: + type: string + enum: + - relevance + - lastUpdatedDate + - submittedDate + description: The sort criterion for the results + default: relevance + sort_order: + type: string + enum: + - ascending + - descending + description: The sort order for the results + default: descending + description: Arguments for Arxiv Search + Tools.ArxivSearchArgumentsUpdate: + type: object + properties: + query: + type: string + description: The search query for searching with Arxiv + id_list: + type: array + items: + type: string + description: The list of Arxiv IDs to search with + max_results: + type: integer + format: int16 + minimum: 1 + maximum: 300000 + description: The maximum number of results to return + default: 5 + download_pdf: + type: boolean + description: The download the pdf of the results + default: false + sort_by: + type: string + enum: + - relevance + - lastUpdatedDate + - submittedDate + description: The sort criterion for the results + default: relevance + sort_order: + type: string + enum: + - ascending + - descending + description: The sort order for the results + default: descending + description: Arguments for Arxiv Search + Tools.BaseBrowserbaseIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - browserbase + default: browserbase + setup: + $ref: '#/components/schemas/Tools.BrowserbaseSetup' + method: + type: string + enum: + - get_live_urls + - list_sessions + - create_session + - get_session + - complete_session + - get_connect_url + - install_extension_from_github + - create_context + - get_session_downloads + - get_logs + - get_recordings + arguments: {} + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: The base definition for a browserbase integration + Tools.BaseBrowserbaseIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - browserbase + default: browserbase + setup: + $ref: '#/components/schemas/Tools.BrowserbaseSetupUpdate' + method: + type: string + enum: + - get_live_urls + - list_sessions + - create_session + - get_session + - complete_session + - get_connect_url + - install_extension_from_github + - create_context + - get_session_downloads + - get_logs + - get_recordings + arguments: {} + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: The base definition for a browserbase integration + Tools.BaseChosenToolCall: + type: object + required: + - type + properties: + type: + allOf: + - $ref: '#/components/schemas/Tools.ToolType' + description: Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + function: + $ref: '#/components/schemas/Tools.FunctionCallOption' + integration: {} + system: {} + api_call: {} + computer_20241022: + allOf: + - $ref: '#/components/schemas/Tools.ChosenComputer20241022' + description: (Alpha) Anthropic new tools + text_editor_20241022: + $ref: '#/components/schemas/Tools.ChosenTextEditor20241022' + bash_20241022: + $ref: '#/components/schemas/Tools.ChosenBash20241022' + id: + type: string + readOnly: true + description: The response tool value generated by the model + Tools.BaseCloudinaryIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - cloudinary + default: cloudinary + setup: + $ref: '#/components/schemas/Tools.CloudinarySetup' + method: + type: string + enum: + - media_upload + - media_edit + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Base Cloudinary integration definition + Tools.BaseCloudinaryIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - cloudinary + default: cloudinary + setup: + $ref: '#/components/schemas/Tools.CloudinarySetupUpdate' + method: + type: string + enum: + - media_upload + - media_edit + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Base Cloudinary integration definition + Tools.BaseIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - dummy + - weather + - wikipedia + - spider + - brave + - browserbase + - email + - remote_browser + - llama_parse + - ffmpeg + - cloudinary + - arxiv + description: The provider of the integration + method: + type: string + description: The specific method of the integration to call + setup: + description: The setup parameters the integration accepts + arguments: + description: The arguments to pre-apply to the integration call + discriminator: + propertyName: provider + mapping: + brave: '#/components/schemas/Tools.BraveIntegrationDef' + email: '#/components/schemas/Tools.EmailIntegrationDef' + spider: '#/components/schemas/Tools.SpiderIntegrationDef' + wikipedia: '#/components/schemas/Tools.WikipediaIntegrationDef' + weather: '#/components/schemas/Tools.WeatherIntegrationDef' + browserbase: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + remote_browser: '#/components/schemas/Tools.RemoteBrowserIntegrationDef' + llama_parse: '#/components/schemas/Tools.LlamaParseIntegrationDef' + ffmpeg: '#/components/schemas/Tools.FfmpegIntegrationDef' + cloudinary: '#/components/schemas/Tools.BaseCloudinaryIntegrationDef' + arxiv: '#/components/schemas/Tools.ArxivIntegrationDef' + description: Integration definition + Tools.BaseIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - dummy + - weather + - wikipedia + - spider + - brave + - browserbase + - email + - remote_browser + - llama_parse + - ffmpeg + - cloudinary + - arxiv + description: The provider of the integration + method: + type: string + description: The specific method of the integration to call + setup: + description: The setup parameters the integration accepts + arguments: + description: The arguments to pre-apply to the integration call + discriminator: + propertyName: provider + mapping: + brave: '#/components/schemas/Tools.BraveIntegrationDefUpdate' + email: '#/components/schemas/Tools.EmailIntegrationDefUpdate' + spider: '#/components/schemas/Tools.SpiderIntegrationDefUpdate' + wikipedia: '#/components/schemas/Tools.WikipediaIntegrationDefUpdate' + weather: '#/components/schemas/Tools.WeatherIntegrationDefUpdate' + browserbase: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + remote_browser: '#/components/schemas/Tools.RemoteBrowserIntegrationDefUpdate' + llama_parse: '#/components/schemas/Tools.LlamaParseIntegrationDefUpdate' + ffmpeg: '#/components/schemas/Tools.FfmpegIntegrationDefUpdate' + cloudinary: '#/components/schemas/Tools.BaseCloudinaryIntegrationDefUpdate' + arxiv: '#/components/schemas/Tools.ArxivIntegrationDefUpdate' + description: Integration definition + Tools.Bash20241022Def: + type: object + required: + - type + properties: + type: + type: string + enum: + - bash_20241022 + default: bash_20241022 + name: + type: string + default: bash + Tools.Bash20241022DefUpdate: + type: object + properties: + type: + type: string + enum: + - bash_20241022 + default: bash_20241022 + name: + type: string + default: bash + Tools.BraveIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - brave + description: The provider must be "brave" + default: brave + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.BraveSearchSetup' + description: The setup parameters for Brave + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BraveSearchArguments' + description: The arguments for Brave Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Brave integration definition + Tools.BraveIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - brave + description: The provider must be "brave" + default: brave + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.BraveSearchSetupUpdate' + description: The setup parameters for Brave + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BraveSearchArgumentsUpdate' + description: The arguments for Brave Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Brave integration definition + Tools.BraveSearchArguments: + type: object + required: + - query + properties: + query: + type: string + description: The search query for searching with Brave + description: Arguments for Brave Search + Tools.BraveSearchArgumentsUpdate: + type: object + properties: + query: + type: string + description: The search query for searching with Brave + description: Arguments for Brave Search + Tools.BraveSearchSetup: + type: object + required: + - api_key + properties: + api_key: + type: string + description: The api key for Brave Search + description: Integration definition for Brave Search + Tools.BraveSearchSetupUpdate: + type: object + properties: + api_key: + type: string + description: The api key for Brave Search + description: Integration definition for Brave Search + Tools.BrowserbaseCompleteSessionArguments: + type: object + required: + - id + properties: + id: + type: string + status: + type: string + enum: + - REQUEST_RELEASE + default: REQUEST_RELEASE + Tools.BrowserbaseCompleteSessionArgumentsUpdate: + type: object + properties: + id: + type: string + status: + type: string + enum: + - REQUEST_RELEASE + default: REQUEST_RELEASE + Tools.BrowserbaseCompleteSessionIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - complete_session + default: complete_session + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase complete session integration definition + Tools.BrowserbaseCompleteSessionIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - complete_session + default: complete_session + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase complete session integration definition + Tools.BrowserbaseContextArguments: + type: object + required: + - projectId + properties: + projectId: + type: string + description: The Project ID. Can be found in Settings. + Tools.BrowserbaseContextArgumentsUpdate: + type: object + properties: + projectId: + type: string + description: The Project ID. Can be found in Settings. + Tools.BrowserbaseContextIntegrationDef: + type: object + properties: + method: + type: string + enum: + - create_context + description: The specific method of the integration to call + default: create_context + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseContextArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase context provider + Tools.BrowserbaseContextIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - create_context + description: The specific method of the integration to call + default: create_context + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseContextArgumentsUpdate' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase context provider + Tools.BrowserbaseCreateSessionArguments: + type: object + properties: + projectId: + type: string + description: The Project ID. Can be found in Settings. + extensionId: + type: string + description: The installed Extension ID. See Install Extension from GitHub. + browserSettings: + type: object + additionalProperties: {} + description: Browser settings + default: {} + timeout: + type: integer + format: uint16 + description: Duration in seconds after which the session will automatically end. Defaults to the Project's defaultTimeout. + default: 3600 + keepAlive: + type: boolean + description: Set to true to keep the session alive even after disconnections. This is available on the Startup plan only. + default: false + proxies: + anyOf: + - type: boolean + - type: array + items: + type: object + additionalProperties: {} + description: Proxy configuration. Can be true for default proxy, or an array of proxy configurations. + default: false + Tools.BrowserbaseCreateSessionIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - create_session + default: create_session + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase create session integration definition + Tools.BrowserbaseCreateSessionIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - create_session + default: create_session + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase create session integration definition + Tools.BrowserbaseExtensionArguments: + type: object + required: + - repositoryName + properties: + repositoryName: + type: string + description: The GitHub repository name. + ref: + type: string + description: Ref to install from a branch or tag. + Tools.BrowserbaseExtensionArgumentsUpdate: + type: object + properties: + repositoryName: + type: string + description: The GitHub repository name. + ref: + type: string + description: Ref to install from a branch or tag. + Tools.BrowserbaseExtensionIntegrationDef: + type: object + properties: + method: + type: string + enum: + - install_extension_from_github + description: The specific method of the integration to call + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase extension provider + Tools.BrowserbaseExtensionIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - install_extension_from_github + description: The specific method of the integration to call + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionArgumentsUpdate' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase extension provider + Tools.BrowserbaseGetSessionArguments: + type: object + required: + - id + properties: + id: + type: string + Tools.BrowserbaseGetSessionArgumentsUpdate: + type: object + properties: + id: + type: string + Tools.BrowserbaseGetSessionConnectUrlArguments: + type: object + required: + - id + properties: + id: + type: string + Tools.BrowserbaseGetSessionConnectUrlArgumentsUpdate: + type: object + properties: + id: + type: string + Tools.BrowserbaseGetSessionConnectUrlIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - get_connect_url + default: get_connect_url + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase get session connect url integration definition + Tools.BrowserbaseGetSessionConnectUrlIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - get_connect_url + default: get_connect_url + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase get session connect url integration definition + Tools.BrowserbaseGetSessionIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - get_session + default: get_session + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase get session integration definition + Tools.BrowserbaseGetSessionIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - get_session + default: get_session + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase get session integration definition + Tools.BrowserbaseGetSessionLiveUrlsArguments: + type: object + required: + - id + properties: + id: + type: string + Tools.BrowserbaseGetSessionLiveUrlsArgumentsUpdate: + type: object + properties: + id: + type: string + Tools.BrowserbaseGetSessionLiveUrlsIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - get_live_urls + default: get_live_urls + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase get session live urls integration definition + Tools.BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - get_live_urls + default: get_live_urls + arguments: + $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase get session live urls integration definition + Tools.BrowserbaseListSessionsArguments: + type: object + properties: + status: + type: string + enum: + - RUNNING + - ERROR + - TIMED_OUT + - COMPLETED + description: 'The status of the sessions to list (Available options: RUNNING, ERROR, TIMED_OUT, COMPLETED)' + Tools.BrowserbaseListSessionsIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - list_sessions + description: The specific method of the integration to call + default: list_sessions + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDef' + description: browserbase list sessions integration definition + Tools.BrowserbaseListSessionsIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - list_sessions + description: The specific method of the integration to call + default: list_sessions + arguments: + allOf: + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsArguments' + description: The arguments for the method + allOf: + - $ref: '#/components/schemas/Tools.BaseBrowserbaseIntegrationDefUpdate' + description: browserbase list sessions integration definition + Tools.BrowserbaseSetup: + type: object + required: + - api_key + - project_id + properties: + api_key: + type: string + description: API key for the browserbase integration + project_id: + type: string + description: The project ID. Can be found in Settings. + api_url: + type: string + description: The API URL. Defaults to https://www.browserbase.com + connect_url: + type: string + description: The connect URL. Defaults to wss://connect.browserbase.com + description: The setup parameters for the browserbase integration + Tools.BrowserbaseSetupUpdate: + type: object + properties: + api_key: + type: string + description: API key for the browserbase integration + project_id: + type: string + description: The project ID. Can be found in Settings. + api_url: + type: string + description: The API URL. Defaults to https://www.browserbase.com + connect_url: + type: string + description: The connect URL. Defaults to wss://connect.browserbase.com + description: The setup parameters for the browserbase integration + Tools.ChosenBash20241022: + type: object + properties: + command: + type: string + description: The bash command to run + restart: + type: boolean + description: Whether to restart the tool + default: false + Tools.ChosenComputer20241022: + type: object + required: + - action + properties: + action: + allOf: + - $ref: '#/components/schemas/Tools.Computer20241022Action' + description: The action to perform + text: + type: string + description: The text to type + coordinate: + type: array + items: + type: integer + format: uint16 + description: The (x, y) pixel coordinate to move the cursor to + Tools.ChosenFunctionCall: + type: object + required: + - type + - function + properties: + type: + type: string + enum: + - function function: allOf: - $ref: '#/components/schemas/Tools.FunctionCallOption' description: The function to call allOf: - - $ref: '#/components/schemas/Tools.ChosenToolCall' - Tools.ChosenToolCall: + - $ref: '#/components/schemas/Tools.BaseChosenToolCall' + Tools.ChosenTextEditor20241022: + type: object + required: + - command + - path + properties: + command: + type: string + enum: + - str_replace + - insert + - view + - undo_edit + description: The command to run + path: + type: string + description: The path to the file + file_text: + type: string + description: The content of the file to be created + insert_line: + type: integer + format: uint16 + description: The line to insert the new string after + new_str: + type: string + description: The new string to insert + old_str: + type: string + description: The string in the file to replace + view_range: + type: array + items: + type: integer + format: uint16 + description: The line range to view + Tools.CloudinaryEditArguments: + type: object + required: + - public_id + - transformation + - return_base64 + properties: + public_id: + type: string + description: The file Public ID in Cloudinary + transformation: + type: array + items: + type: object + additionalProperties: {} + description: The transformation to apply to the file + return_base64: + type: boolean + description: Return base64 encoded file + default: false + description: Arguments for Cloudinary media edit + Tools.CloudinaryEditArgumentsUpdate: + type: object + properties: + public_id: + type: string + description: The file Public ID in Cloudinary + transformation: + type: array + items: + type: object + additionalProperties: {} + description: The transformation to apply to the file + return_base64: + type: boolean + description: Return base64 encoded file + default: false + description: Arguments for Cloudinary media edit + Tools.CloudinaryEditIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - media_edit + default: media_edit + arguments: + $ref: '#/components/schemas/Tools.CloudinaryEditArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseCloudinaryIntegrationDef' + description: Cloudinary edit integration definition + Tools.CloudinaryEditIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - media_edit + default: media_edit + arguments: + $ref: '#/components/schemas/Tools.CloudinaryEditArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseCloudinaryIntegrationDefUpdate' + description: Cloudinary edit integration definition + Tools.CloudinarySetup: + type: object + required: + - cloudinary_api_key + - cloudinary_api_secret + - cloudinary_cloud_name + properties: + cloudinary_api_key: + type: string + description: The API key for Cloudinary + cloudinary_api_secret: + type: string + description: The API secret for Cloudinary + cloudinary_cloud_name: + type: string + description: The Cloud name for Cloudinary + params: + type: object + additionalProperties: {} + description: Additional parameters for the Cloudinary API + description: Setup parameters for Cloudinary integration + Tools.CloudinarySetupUpdate: + type: object + properties: + cloudinary_api_key: + type: string + description: The API key for Cloudinary + cloudinary_api_secret: + type: string + description: The API secret for Cloudinary + cloudinary_cloud_name: + type: string + description: The Cloud name for Cloudinary + params: + type: object + additionalProperties: {} + description: Additional parameters for the Cloudinary API + description: Setup parameters for Cloudinary integration + Tools.CloudinaryUploadArguments: + type: object + required: + - file + - return_base64 + properties: + file: + type: string + description: The URL of the file upload + return_base64: + type: boolean + description: Return base64 encoded file + default: false + public_id: + type: string + description: Optional public ID for the uploaded file + upload_params: + type: object + additionalProperties: {} + description: Optional upload parameters + description: Arguments for Cloudinary media upload + Tools.CloudinaryUploadArgumentsUpdate: + type: object + properties: + file: + type: string + description: The URL of the file upload + return_base64: + type: boolean + description: Return base64 encoded file + default: false + public_id: + type: string + description: Optional public ID for the uploaded file + upload_params: + type: object + additionalProperties: {} + description: Optional upload parameters + description: Arguments for Cloudinary media upload + Tools.CloudinaryUploadIntegrationDef: + type: object + required: + - method + properties: + method: + type: string + enum: + - media_upload + default: media_upload + arguments: + $ref: '#/components/schemas/Tools.CloudinaryUploadArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseCloudinaryIntegrationDef' + description: Cloudinary upload integration definition + Tools.CloudinaryUploadIntegrationDefUpdate: + type: object + properties: + method: + type: string + enum: + - media_upload + default: media_upload + arguments: + $ref: '#/components/schemas/Tools.CloudinaryUploadArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseCloudinaryIntegrationDefUpdate' + description: Cloudinary upload integration definition + Tools.Computer20241022Action: + type: string + enum: + - key + - type + - cursor_position + - mouse_move + - left_click + - right_click + - middle_click + - double_click + - screenshot + Tools.Computer20241022Def: type: object required: - type - - id + - display_width_px + - display_height_px properties: type: - allOf: - - $ref: '#/components/schemas/Tools.ToolType' - description: Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - function: - $ref: '#/components/schemas/Tools.FunctionCallOption' - id: - allOf: - - $ref: '#/components/schemas/Common.uuid' - readOnly: true - discriminator: - propertyName: type - mapping: - function: '#/components/schemas/Tools.ChosenFunctionCall' - description: The response tool value generated by the model + type: string + enum: + - computer_20241022 + default: computer_20241022 + name: + type: string + default: computer + display_width_px: + type: integer + format: uint16 + minimum: 600 + description: The display width in pixels + default: 1024 + display_height_px: + type: integer + format: uint16 + minimum: 400 + description: The display height in pixels + default: 768 + display_number: + type: integer + format: uint16 + minimum: 1 + maximum: 10 + description: The display number to use + default: 1 + description: Anthropic new tools + Tools.Computer20241022DefUpdate: + type: object + properties: + type: + type: string + enum: + - computer_20241022 + default: computer_20241022 + name: + type: string + default: computer + display_width_px: + type: integer + format: uint16 + minimum: 600 + description: The display width in pixels + default: 1024 + display_height_px: + type: integer + format: uint16 + minimum: 400 + description: The display height in pixels + default: 768 + display_number: + type: integer + format: uint16 + minimum: 1 + maximum: 10 + description: The display number to use + default: 1 + description: Anthropic new tools Tools.CreateToolRequest: type: object required: - name + - type properties: name: allOf: - $ref: '#/components/schemas/Common.validPythonIdentifier' description: Name of the tool (must be unique for this agent and a valid python identifier string ) + type: + allOf: + - $ref: '#/components/schemas/Tools.ToolType' + description: Type of the tool description: type: string description: Description of the tool @@ -5801,8 +7785,27 @@ components: - $ref: '#/components/schemas/Tools.FunctionDef' description: The function to call integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' + anyOf: + - $ref: '#/components/schemas/Tools.DummyIntegrationDef' + - $ref: '#/components/schemas/Tools.BraveIntegrationDef' + - $ref: '#/components/schemas/Tools.EmailIntegrationDef' + - $ref: '#/components/schemas/Tools.SpiderIntegrationDef' + - $ref: '#/components/schemas/Tools.WikipediaIntegrationDef' + - $ref: '#/components/schemas/Tools.WeatherIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseContextIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlIntegrationDef' + - $ref: '#/components/schemas/Tools.RemoteBrowserIntegrationDef' + - $ref: '#/components/schemas/Tools.LlamaParseIntegrationDef' + - $ref: '#/components/schemas/Tools.FfmpegIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryUploadIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryEditIntegrationDef' + - $ref: '#/components/schemas/Tools.ArxivIntegrationDef' description: The integration to call system: allOf: @@ -5812,7 +7815,231 @@ components: allOf: - $ref: '#/components/schemas/Tools.ApiCallDef' description: The API call to make + computer_20241022: + allOf: + - $ref: '#/components/schemas/Tools.Computer20241022Def' + description: (Alpha) Anthropic new tools + text_editor_20241022: + $ref: '#/components/schemas/Tools.TextEditor20241022Def' + bash_20241022: + $ref: '#/components/schemas/Tools.Bash20241022Def' description: Payload for creating a tool + Tools.DummyIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - dummy + default: dummy + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + Tools.DummyIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - dummy + default: dummy + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + Tools.EmailArguments: + type: object + required: + - to + - from + - subject + - body + properties: + to: + type: string + description: The email address to send the email to + from: + type: string + description: The email address to send the email from + subject: + type: string + description: The subject of the email + body: + type: string + description: The body of the email + description: Arguments for Email sending + Tools.EmailArgumentsUpdate: + type: object + properties: + to: + type: string + description: The email address to send the email to + from: + type: string + description: The email address to send the email from + subject: + type: string + description: The subject of the email + body: + type: string + description: The body of the email + description: Arguments for Email sending + Tools.EmailIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - email + description: The provider must be "email" + default: email + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.EmailSetup' + description: The setup parameters for Email + arguments: + allOf: + - $ref: '#/components/schemas/Tools.EmailArguments' + description: The arguments for Email sending + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Email integration definition + Tools.EmailIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - email + description: The provider must be "email" + default: email + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.EmailSetupUpdate' + description: The setup parameters for Email + arguments: + allOf: + - $ref: '#/components/schemas/Tools.EmailArgumentsUpdate' + description: The arguments for Email sending + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Email integration definition + Tools.EmailSetup: + type: object + required: + - host + - port + - user + - password + properties: + host: + type: string + description: The host of the email server + port: + type: integer + format: int32 + description: The port of the email server + user: + type: string + description: The username of the email server + password: + type: string + description: The password of the email server + description: Setup parameters for Email integration + Tools.EmailSetupUpdate: + type: object + properties: + host: + type: string + description: The host of the email server + port: + type: integer + format: int32 + description: The port of the email server + user: + type: string + description: The username of the email server + password: + type: string + description: The password of the email server + description: Setup parameters for Email integration + Tools.FfmpegIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - ffmpeg + description: The provider must be "ffmpeg" + default: ffmpeg + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Ffmpeg + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.FfmpegSearchArguments' + description: The arguments for Ffmpeg Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Ffmpeg integration definition + Tools.FfmpegIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - ffmpeg + description: The provider must be "ffmpeg" + default: ffmpeg + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Ffmpeg + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.FfmpegSearchArgumentsUpdate' + description: The arguments for Ffmpeg Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Ffmpeg integration definition + Tools.FfmpegSearchArguments: + type: object + required: + - cmd + properties: + cmd: + type: string + description: The bash command string + file: + type: string + description: The base64 string of the file + description: Arguments for Ffmpeg CMD + Tools.FfmpegSearchArgumentsUpdate: + type: object + properties: + cmd: + type: string + description: The bash command string + file: + type: string + description: The base64 string of the file + description: Arguments for Ffmpeg CMD Tools.FunctionCallOption: type: object required: @@ -5821,6 +8048,9 @@ components: name: type: string description: The name of the function + arguments: + type: string + description: The parameters to pass to the function Tools.FunctionDef: type: object properties: @@ -5837,66 +8067,124 @@ components: additionalProperties: {} description: The parameters the function accepts description: Function definition - Tools.IntegrationDef: + Tools.LlamaParseFetchArguments: type: object required: - - provider + - file properties: - provider: + filename: + type: string + description: File Name. If not provided, a random name will be generated. + file: anyOf: - type: string - enum: - - dummy - - hacker_news - - weather - - wikipedia - - spider - - brave - - browserbase - - email + - type: array + items: + type: string + description: The base64 string of the file, which can be a single string or a list of strings + params: + type: object + additionalProperties: {} + description: Optional upload parameters + base64: + type: boolean + description: The input file is base64 + default: false + description: Arguments for LlamaParse integration + Tools.LlamaParseFetchArgumentsUpdate: + type: object + properties: + filename: + type: string + description: File Name. If not provided, a random name will be generated. + file: + anyOf: - type: string - description: The provider of the integration + - type: array + items: + type: string + description: The base64 string of the file, which can be a single string or a list of strings + params: + type: object + additionalProperties: {} + description: Optional upload parameters + base64: + type: boolean + description: The input file is base64 + default: false + description: Arguments for LlamaParse integration + Tools.LlamaParseIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - llama_parse + description: The provider must be "LlamaParseSetup" + default: llama_parse method: type: string description: The specific method of the integration to call setup: - type: object - additionalProperties: {} - description: The setup parameters the integration accepts - arguments: - type: object - additionalProperties: {} - description: The arguments to pre-apply to the integration call - description: Integration definition - Tools.IntegrationDefUpdate: + allOf: + - $ref: '#/components/schemas/Tools.LlamaParseSetup' + description: The setup parameters for LlamaParse + arguments: + allOf: + - $ref: '#/components/schemas/Tools.LlamaParseFetchArguments' + description: The arguments for LlamaParse + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: LlamaParse integration definition + Tools.LlamaParseIntegrationDefUpdate: type: object properties: provider: - anyOf: - - type: string - enum: - - dummy - - hacker_news - - weather - - wikipedia - - spider - - brave - - browserbase - - email - - type: string - description: The provider of the integration + type: string + enum: + - llama_parse + description: The provider must be "LlamaParseSetup" + default: llama_parse method: type: string description: The specific method of the integration to call setup: + allOf: + - $ref: '#/components/schemas/Tools.LlamaParseSetupUpdate' + description: The setup parameters for LlamaParse + arguments: + allOf: + - $ref: '#/components/schemas/Tools.LlamaParseFetchArgumentsUpdate' + description: The arguments for LlamaParse + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: LlamaParse integration definition + Tools.LlamaParseSetup: + type: object + required: + - llamaparse_api_key + properties: + llamaparse_api_key: + type: string + description: The API key for LlamaParse + params: type: object additionalProperties: {} - description: The setup parameters the integration accepts - arguments: + description: Optional upload parameters + description: Setup parameters for LlamaParse integration + Tools.LlamaParseSetupUpdate: + type: object + properties: + llamaparse_api_key: + type: string + description: The API key for LlamaParse + params: type: object additionalProperties: {} - description: The arguments to pre-apply to the integration call - description: Integration definition + description: Optional upload parameters + description: Setup parameters for LlamaParse integration Tools.NamedToolChoice: type: object properties: @@ -5909,6 +8197,10 @@ components: allOf: - $ref: '#/components/schemas/Common.validPythonIdentifier' description: Name of the tool (must be unique for this agent and a valid python identifier string ) + type: + allOf: + - $ref: '#/components/schemas/Tools.ToolType' + description: Type of the tool description: type: string description: Description of the tool @@ -5917,8 +8209,27 @@ components: - $ref: '#/components/schemas/Tools.FunctionDef' description: The function to call integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDefUpdate' + anyOf: + - $ref: '#/components/schemas/Tools.DummyIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BraveIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.EmailIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.SpiderIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.WikipediaIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.WeatherIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseContextIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.RemoteBrowserIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.LlamaParseIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.FfmpegIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.CloudinaryUploadIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.CloudinaryEditIntegrationDefUpdate' + - $ref: '#/components/schemas/Tools.ArxivIntegrationDefUpdate' description: The integration to call system: allOf: @@ -5928,7 +8239,238 @@ components: allOf: - $ref: '#/components/schemas/Tools.ApiCallDefUpdate' description: The API call to make + computer_20241022: + allOf: + - $ref: '#/components/schemas/Tools.Computer20241022DefUpdate' + description: (Alpha) Anthropic new tools + text_editor_20241022: + $ref: '#/components/schemas/Tools.TextEditor20241022DefUpdate' + bash_20241022: + $ref: '#/components/schemas/Tools.Bash20241022DefUpdate' description: Payload for patching a tool + Tools.RemoteBrowserArguments: + type: object + required: + - action + properties: + connect_url: + type: string + description: The connection URL for the remote browser + action: + type: string + enum: + - key + - type + - mouse_move + - left_click + - left_click_drag + - right_click + - middle_click + - double_click + - screenshot + - cursor_position + - navigate + - refresh + description: The action to perform + text: + type: string + description: The text + coordinate: + type: array + items: {} + description: The coordinate to move the mouse to + description: The arguments for the remote browser + Tools.RemoteBrowserArgumentsUpdate: + type: object + properties: + connect_url: + type: string + description: The connection URL for the remote browser + action: + type: string + enum: + - key + - type + - mouse_move + - left_click + - left_click_drag + - right_click + - middle_click + - double_click + - screenshot + - cursor_position + - navigate + - refresh + description: The action to perform + text: + type: string + description: The text + coordinate: + type: array + items: {} + description: The coordinate to move the mouse to + description: The arguments for the remote browser + Tools.RemoteBrowserIntegrationDef: + type: object + required: + - provider + - setup + - method + properties: + provider: + type: string + enum: + - remote_browser + default: remote_browser + setup: + $ref: '#/components/schemas/Tools.RemoteBrowserSetup' + method: + type: string + enum: + - perform_action + default: perform_action + arguments: + $ref: '#/components/schemas/Tools.RemoteBrowserArguments' + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: The integration definition for the remote browser + Tools.RemoteBrowserIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - remote_browser + default: remote_browser + setup: + $ref: '#/components/schemas/Tools.RemoteBrowserSetup' + method: + type: string + enum: + - perform_action + default: perform_action + arguments: + $ref: '#/components/schemas/Tools.RemoteBrowserArgumentsUpdate' + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: The integration definition for the remote browser + Tools.RemoteBrowserSetup: + type: object + properties: + connect_url: + type: string + description: The connection URL for the remote browser + width: + type: integer + format: uint16 + description: The width of the browser + height: + type: integer + format: uint16 + description: The height of the browser + description: The setup parameters for the remote browser + Tools.SpiderFetchArguments: + type: object + required: + - url + properties: + url: + type: string + format: uri + description: The URL to fetch data from + mode: + type: string + enum: + - scrape + description: The type of crawler to use + default: scrape + params: + type: object + additionalProperties: {} + description: Additional parameters for the Spider API + description: Arguments for Spider integration + Tools.SpiderFetchArgumentsUpdate: + type: object + properties: + url: + type: string + format: uri + description: The URL to fetch data from + mode: + type: string + enum: + - scrape + description: The type of crawler to use + default: scrape + params: + type: object + additionalProperties: {} + description: Additional parameters for the Spider API + description: Arguments for Spider integration + Tools.SpiderIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - spider + description: The provider must be "spider" + default: spider + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.SpiderSetup' + description: The setup parameters for Spider + arguments: + allOf: + - $ref: '#/components/schemas/Tools.SpiderFetchArguments' + description: The arguments for Spider + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Spider integration definition + Tools.SpiderIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - spider + description: The provider must be "spider" + default: spider + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.SpiderSetupUpdate' + description: The setup parameters for Spider + arguments: + allOf: + - $ref: '#/components/schemas/Tools.SpiderFetchArgumentsUpdate' + description: The arguments for Spider + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Spider integration definition + Tools.SpiderSetup: + type: object + required: + - spider_api_key + properties: + spider_api_key: + type: string + description: The API key for Spider + description: Setup parameters for Spider integration + Tools.SpiderSetupUpdate: + type: object + properties: + spider_api_key: + type: string + description: The API key for Spider + description: Setup parameters for Spider integration Tools.SystemDef: type: object required: @@ -6026,10 +8568,35 @@ components: additionalProperties: {} description: The arguments to pre-apply to the system call description: System definition + Tools.TextEditor20241022Def: + type: object + required: + - type + properties: + type: + type: string + enum: + - text_editor_20241022 + default: text_editor_20241022 + name: + type: string + default: str_replace_editor + Tools.TextEditor20241022DefUpdate: + type: object + properties: + type: + type: string + enum: + - text_editor_20241022 + default: text_editor_20241022 + name: + type: string + default: str_replace_editor Tools.Tool: type: object required: - name + - type - created_at - updated_at - id @@ -6038,6 +8605,10 @@ components: allOf: - $ref: '#/components/schemas/Common.validPythonIdentifier' description: Name of the tool (must be unique for this agent and a valid python identifier string ) + type: + allOf: + - $ref: '#/components/schemas/Tools.ToolType' + description: Type of the tool description: type: string description: Description of the tool @@ -6046,8 +8617,27 @@ components: - $ref: '#/components/schemas/Tools.FunctionDef' description: The function to call integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' + anyOf: + - $ref: '#/components/schemas/Tools.DummyIntegrationDef' + - $ref: '#/components/schemas/Tools.BraveIntegrationDef' + - $ref: '#/components/schemas/Tools.EmailIntegrationDef' + - $ref: '#/components/schemas/Tools.SpiderIntegrationDef' + - $ref: '#/components/schemas/Tools.WikipediaIntegrationDef' + - $ref: '#/components/schemas/Tools.WeatherIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseContextIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlIntegrationDef' + - $ref: '#/components/schemas/Tools.RemoteBrowserIntegrationDef' + - $ref: '#/components/schemas/Tools.LlamaParseIntegrationDef' + - $ref: '#/components/schemas/Tools.FfmpegIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryUploadIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryEditIntegrationDef' + - $ref: '#/components/schemas/Tools.ArxivIntegrationDef' description: The integration to call system: allOf: @@ -6057,6 +8647,14 @@ components: allOf: - $ref: '#/components/schemas/Tools.ApiCallDef' description: The API call to make + computer_20241022: + allOf: + - $ref: '#/components/schemas/Tools.Computer20241022Def' + description: (Alpha) Anthropic new tools + text_editor_20241022: + $ref: '#/components/schemas/Tools.TextEditor20241022Def' + bash_20241022: + $ref: '#/components/schemas/Tools.Bash20241022Def' created_at: type: string format: date-time @@ -6078,7 +8676,7 @@ components: - output properties: id: - $ref: '#/components/schemas/Common.uuid' + type: string output: type: object additionalProperties: {} @@ -6090,15 +8688,23 @@ components: - integration - system - api_call + - computer_20241022 + - text_editor_20241022 + - bash_20241022 Tools.UpdateToolRequest: type: object required: - name + - type properties: name: allOf: - $ref: '#/components/schemas/Common.validPythonIdentifier' description: Name of the tool (must be unique for this agent and a valid python identifier string ) + type: + allOf: + - $ref: '#/components/schemas/Tools.ToolType' + description: Type of the tool description: type: string description: Description of the tool @@ -6107,8 +8713,27 @@ components: - $ref: '#/components/schemas/Tools.FunctionDef' description: The function to call integration: - allOf: - - $ref: '#/components/schemas/Tools.IntegrationDef' + anyOf: + - $ref: '#/components/schemas/Tools.DummyIntegrationDef' + - $ref: '#/components/schemas/Tools.BraveIntegrationDef' + - $ref: '#/components/schemas/Tools.EmailIntegrationDef' + - $ref: '#/components/schemas/Tools.SpiderIntegrationDef' + - $ref: '#/components/schemas/Tools.WikipediaIntegrationDef' + - $ref: '#/components/schemas/Tools.WeatherIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseContextIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseExtensionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseListSessionsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCreateSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseCompleteSessionIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionLiveUrlsIntegrationDef' + - $ref: '#/components/schemas/Tools.BrowserbaseGetSessionConnectUrlIntegrationDef' + - $ref: '#/components/schemas/Tools.RemoteBrowserIntegrationDef' + - $ref: '#/components/schemas/Tools.LlamaParseIntegrationDef' + - $ref: '#/components/schemas/Tools.FfmpegIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryUploadIntegrationDef' + - $ref: '#/components/schemas/Tools.CloudinaryEditIntegrationDef' + - $ref: '#/components/schemas/Tools.ArxivIntegrationDef' description: The integration to call system: allOf: @@ -6118,7 +8743,174 @@ components: allOf: - $ref: '#/components/schemas/Tools.ApiCallDef' description: The API call to make + computer_20241022: + allOf: + - $ref: '#/components/schemas/Tools.Computer20241022Def' + description: (Alpha) Anthropic new tools + text_editor_20241022: + $ref: '#/components/schemas/Tools.TextEditor20241022Def' + bash_20241022: + $ref: '#/components/schemas/Tools.Bash20241022Def' description: Payload for updating a tool + Tools.WeatherGetArguments: + type: object + required: + - location + properties: + location: + type: string + description: The location for which to fetch weather data + description: Arguments for Weather + Tools.WeatherGetArgumentsUpdate: + type: object + properties: + location: + type: string + description: The location for which to fetch weather data + description: Arguments for Weather + Tools.WeatherIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - weather + description: The provider must be "weather" + default: weather + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.WeatherSetup' + description: The setup parameters for Weather + arguments: + allOf: + - $ref: '#/components/schemas/Tools.WeatherGetArguments' + description: The arguments for Weather + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Weather integration definition + Tools.WeatherIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - weather + description: The provider must be "weather" + default: weather + method: + type: string + description: The specific method of the integration to call + setup: + allOf: + - $ref: '#/components/schemas/Tools.WeatherSetupUpdate' + description: The setup parameters for Weather + arguments: + allOf: + - $ref: '#/components/schemas/Tools.WeatherGetArgumentsUpdate' + description: The arguments for Weather + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Weather integration definition + Tools.WeatherSetup: + type: object + required: + - openweathermap_api_key + properties: + openweathermap_api_key: + type: string + description: The api key for OpenWeatherMap + description: Integration definition for Weather + Tools.WeatherSetupUpdate: + type: object + properties: + openweathermap_api_key: + type: string + description: The api key for OpenWeatherMap + description: Integration definition for Weather + Tools.WikipediaIntegrationDef: + type: object + required: + - provider + properties: + provider: + type: string + enum: + - wikipedia + description: The provider must be "wikipedia" + default: wikipedia + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Wikipedia + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.WikipediaSearchArguments' + description: The arguments for Wikipedia Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDef' + description: Wikipedia integration definition + Tools.WikipediaIntegrationDefUpdate: + type: object + properties: + provider: + type: string + enum: + - wikipedia + description: The provider must be "wikipedia" + default: wikipedia + method: + type: string + description: The specific method of the integration to call + setup: + nullable: true + description: The setup parameters for Wikipedia + default: null + arguments: + allOf: + - $ref: '#/components/schemas/Tools.WikipediaSearchArgumentsUpdate' + description: The arguments for Wikipedia Search + allOf: + - $ref: '#/components/schemas/Tools.BaseIntegrationDefUpdate' + description: Wikipedia integration definition + Tools.WikipediaSearchArguments: + type: object + required: + - query + - load_max_docs + properties: + query: + type: string + description: The search query string + load_max_docs: + type: integer + format: uint8 + minimum: 1 + maximum: 10 + description: Maximum number of documents to load + default: 2 + description: Arguments for Wikipedia Search + Tools.WikipediaSearchArgumentsUpdate: + type: object + properties: + query: + type: string + description: The search query string + load_max_docs: + type: integer + format: uint8 + minimum: 1 + maximum: 10 + description: Maximum number of documents to load + default: 2 + description: Arguments for Wikipedia Search Users.CreateOrUpdateUserRequest: type: object required: diff --git a/typespec/tspconfig.yaml b/typespec/tspconfig.yaml index c5b3491f6..7bd4e8d1e 100644 --- a/typespec/tspconfig.yaml +++ b/typespec/tspconfig.yaml @@ -6,4 +6,4 @@ options: file-type: yaml output-file: "openapi-{version}.yaml" new-line: lf - # omit-unreachable-types: true + omit-unreachable-types: true diff --git a/typespec/versions.tsp b/typespec/versions.tsp index 4739bbdf2..488e9927a 100644 --- a/typespec/versions.tsp +++ b/typespec/versions.tsp @@ -1,6 +1,5 @@ namespace Versions; enum ApiVersions { - v0_4: "0.4.0", v1_0: "1.0.0", }