feat: uv migration (#3493)
* uv migration smaller runners, freeze test runs, remove dev, ruff,hatchling, previw, poetry, generates wheel, installs wheel, docker * fix tests and dependency groups * test fixes * test fixing and main * resolve merge conflict * dev + test dependency group * Test * trigger CI * trigger CI * add debugging info * trigger CI * uv for reusable and sdk preview * resolve mc and reformat black * staged-api * mypy * fix fern * prod Dockerfile * model sweep, and project.toml and uvlock * --group test -> --extra dev * remove redundant --extra dev and rename tests to dev * sdk backwards compat install sqlite * install sqlite group for sdk-backwards-compat * install uv on gh runner for cloud-api-integration-tests * stage+publish * pytest asyncio * bug causing pytest package to get removed * try to fix async event loop issues * migrate to --with google-cloud-secret-manager --------- Co-authored-by: Kian Jones <kian@letta.com>
This commit is contained in:
25
.github/workflows/code_style_checks.yml
vendored
25
.github/workflows/code_style_checks.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11", "3.12", "3.13"] # Adjust Python version matrix if needed
|
||||
python-version: ["3.11"] # Removed 3.12+ as minimal sets the standard. Adjust Python version matrix if needed
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -20,12 +20,21 @@ jobs:
|
||||
ref: ${{ github.head_ref }} # Checkout the PR branch
|
||||
fetch-depth: 0 # Fetch all history for all branches and tags
|
||||
|
||||
- name: "Setup Python, Poetry and Dependencies"
|
||||
uses: packetcoders/action-setup-cache-python-poetry@main
|
||||
- name: Set up python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: "2.1.3"
|
||||
install-args: "-E dev -E postgres -E external-tools -E tests" # Adjust as necessary
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
activate-environment: true
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
uv sync --extra dev --extra postgres --extra external-tools --extra dev
|
||||
|
||||
- name: Validate PR Title
|
||||
if: github.event_name == 'pull_request'
|
||||
@@ -41,10 +50,10 @@ jobs:
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run isort
|
||||
run: poetry run isort --profile black --check-only --diff .
|
||||
run: uv run isort --profile black --check-only --diff .
|
||||
|
||||
- name: Run Black
|
||||
run: poetry run black --check .
|
||||
run: uv run black --check .
|
||||
|
||||
- name: Run Autoflake
|
||||
run: poetry run autoflake --remove-all-unused-imports --remove-unused-variables --in-place --recursive --ignore-init-module-imports .
|
||||
run: uv run autoflake --remove-all-unused-imports --remove-unused-variables --in-place --recursive --ignore-init-module-imports .
|
||||
|
||||
22
.github/workflows/docker-integration-tests.yaml
vendored
22
.github/workflows/docker-integration-tests.yaml
vendored
@@ -13,10 +13,17 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Python
|
||||
|
||||
- name: Set up python 3.11
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
- name: Set permissions for log directory
|
||||
run: |
|
||||
@@ -34,12 +41,6 @@ jobs:
|
||||
|
||||
run: |
|
||||
docker compose -f dev-compose.yaml up --build -d
|
||||
#- name: "Setup Python, Poetry and Dependencies"
|
||||
# uses: packetcoders/action-setup-cache-python-poetry@v1.2.0
|
||||
# with:
|
||||
# python-version: "3.11"
|
||||
# poetry-version: "1.8.2"
|
||||
# install-args: "--all-extras"
|
||||
|
||||
- name: Wait for service
|
||||
run: bash scripts/wait_for_service.sh http://localhost:8283 -- echo "Service is ready"
|
||||
@@ -55,9 +56,8 @@ jobs:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
PYTHONPATH: ${{ github.workspace }}:${{ env.PYTHONPATH }}
|
||||
run: |
|
||||
pipx install poetry==2.1.3
|
||||
poetry install -E dev -E postgres
|
||||
poetry run pytest -s tests/test_client.py
|
||||
uv sync --extra dev --extra postgres
|
||||
uv run pytest -s tests/test_client.py
|
||||
|
||||
- name: Print docker logs if tests fail
|
||||
if: failure()
|
||||
|
||||
24
.github/workflows/migration-test.yml
vendored
24
.github/workflows/migration-test.yml
vendored
@@ -26,12 +26,22 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- run: psql -h localhost -U postgres -d postgres -c 'CREATE EXTENSION vector'
|
||||
- name: "Setup Python, Poetry and Dependencies"
|
||||
uses: packetcoders/action-setup-cache-python-poetry@main
|
||||
|
||||
- name: Set up python 3.11
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: "1.8.2"
|
||||
install-args: "--all-extras"
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
uv sync --all-extras
|
||||
|
||||
- name: Test alembic migration
|
||||
env:
|
||||
LETTA_PG_PORT: 5432
|
||||
@@ -40,5 +50,5 @@ jobs:
|
||||
LETTA_PG_DB: postgres
|
||||
LETTA_PG_HOST: localhost
|
||||
run: |
|
||||
poetry run alembic upgrade head
|
||||
poetry run alembic check
|
||||
uv run alembic upgrade head
|
||||
uv run alembic check
|
||||
|
||||
8
.github/workflows/model-sweep.yaml
vendored
8
.github/workflows/model-sweep.yaml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: poetry install --no-interaction --no-root ${{ inputs.install-args || '-E dev -E postgres -E external-tools -E tests -E cloud-tool-sandbox -E google' }}
|
||||
run: uv sync --extra dev --extra postgres --extra external-tools --extra cloud-tool-sandbox --extra google
|
||||
- name: Migrate database
|
||||
env:
|
||||
LETTA_PG_PORT: 5432
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
LETTA_PG_HOST: localhost
|
||||
run: |
|
||||
psql -h localhost -U postgres -d postgres -c 'CREATE EXTENSION vector'
|
||||
poetry run alembic upgrade head
|
||||
uv run alembic upgrade head
|
||||
|
||||
- name: Run integration tests
|
||||
# if any of the 1000+ test cases fail, pytest reports exit code 1 and won't procces/upload the results
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
DEEPSEEK_API_KEY: ${{ env.DEEPSEEK_API_KEY}}
|
||||
LETTA_USE_EXPERIMENTAL: 1
|
||||
run: |
|
||||
poetry run pytest \
|
||||
uv run pytest \
|
||||
-s -vv \
|
||||
.github/scripts/model-sweep/model_sweep.py \
|
||||
--json-report --json-report-file=.github/scripts/model-sweep/model_sweep_report.json --json-report-indent=4
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
# file path args to generate_model_sweep_markdown.py are relative to the script
|
||||
run: |
|
||||
poetry run python \
|
||||
uv run python \
|
||||
.github/scripts/model-sweep/generate_model_sweep_markdown.py \
|
||||
.github/scripts/model-sweep/model_sweep_report.json \
|
||||
.github/scripts/model-sweep/supported-models.mdx
|
||||
|
||||
27
.github/workflows/poetry-publish-nightly.yml
vendored
27
.github/workflows/poetry-publish-nightly.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: poetry-publish-nightly
|
||||
name: uv-publish-nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: '35 10 * * *' # 10:35am UTC, 2:35am PST, 5:35am EST
|
||||
@@ -31,11 +31,17 @@ jobs:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Setup Python, Poetry and Dependencies"
|
||||
uses: packetcoders/action-setup-cache-python-poetry@main
|
||||
- name: Set up python 3.12
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: "1.7.1"
|
||||
python-version: 3.12
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
activate-environment: true
|
||||
|
||||
- name: Set release version
|
||||
run: |
|
||||
@@ -50,13 +56,10 @@ jobs:
|
||||
cat pyproject.toml
|
||||
cat letta/__init__.py
|
||||
|
||||
- name: Configure poetry
|
||||
env:
|
||||
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN}}
|
||||
run: poetry config pypi-token.pypi "$PYPI_TOKEN"
|
||||
|
||||
- name: Build the Python package
|
||||
run: poetry build
|
||||
run: uv build
|
||||
|
||||
- name: Publish the package to PyPI
|
||||
run: poetry publish
|
||||
env:
|
||||
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||
run: uv publish
|
||||
|
||||
27
.github/workflows/poetry-publish.yml
vendored
27
.github/workflows/poetry-publish.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: poetry-publish
|
||||
name: uv-publish
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
@@ -13,20 +13,23 @@ jobs:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Setup Python, Poetry and Dependencies"
|
||||
uses: packetcoders/action-setup-cache-python-poetry@main
|
||||
- name: Set up python 3.12
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: "1.7.1"
|
||||
python-version: 3.12
|
||||
|
||||
- name: Configure poetry
|
||||
env:
|
||||
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||
run: |
|
||||
poetry config pypi-token.pypi "$PYPI_TOKEN"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
activate-environment: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Build the Python package
|
||||
run: poetry build
|
||||
run: uv build
|
||||
|
||||
- name: Publish the package to PyPI
|
||||
run: poetry publish
|
||||
env:
|
||||
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||
run: uv publish
|
||||
|
||||
@@ -100,35 +100,25 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Load cached Poetry Binary
|
||||
id: cached-poetry-binary
|
||||
uses: actions/cache@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
path: ~/.local
|
||||
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-1.8.3
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.8.3
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
version: "latest"
|
||||
- name: Load cached venv
|
||||
id: cached-poetry-dependencies
|
||||
id: cached-uv-dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .venv
|
||||
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}${{ inputs.install-args || '-E dev -E postgres -E external-tools -E tests -E cloud-tool-sandbox' }}
|
||||
# Restore cache with this prefix if not exact match with key
|
||||
# Note cache-hit returns false in this case, so the below step will run
|
||||
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/uv.lock') }}
|
||||
restore-keys: |
|
||||
venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-
|
||||
- name: Install dependencies
|
||||
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
if: steps.cached-uv-dependencies.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: poetry install --no-interaction --no-root ${{ inputs.install-args || '-E dev -E postgres -E external-tools -E tests -E cloud-tool-sandbox -E google' }}
|
||||
- name: Install letta packages via Poetry
|
||||
run: uv sync --extra dev --extra postgres --extra external-tools --extra cloud-tool-sandbox --extra google
|
||||
- name: Install letta packages
|
||||
run: |
|
||||
poetry run pip install --upgrade letta-client letta
|
||||
uv run pip install --upgrade letta-client letta
|
||||
- name: Migrate database
|
||||
env:
|
||||
LETTA_PG_PORT: 5432
|
||||
@@ -138,7 +128,7 @@ jobs:
|
||||
LETTA_PG_HOST: localhost
|
||||
run: |
|
||||
psql -h localhost -U postgres -d postgres -c 'CREATE EXTENSION vector'
|
||||
poetry run alembic upgrade head
|
||||
uv run alembic upgrade head
|
||||
- name: Run integration tests for ${{ matrix.config_file }}
|
||||
env:
|
||||
LLM_CONFIG_FILE: ${{ matrix.config_file }}
|
||||
@@ -161,7 +151,7 @@ jobs:
|
||||
GOOGLE_CLOUD_LOCATION: ${{ secrets.GOOGLE_CLOUD_LOCATION }}
|
||||
LETTA_GEMINI_FORCE_MINIMUM_THINKING_BUDGET: true
|
||||
run: |
|
||||
poetry run pytest \
|
||||
uv run pytest \
|
||||
-s -vv \
|
||||
tests/integration_test_send_message.py \
|
||||
--maxfail=1 --durations=10
|
||||
|
||||
28
.github/workflows/warn_poetry_updates.yml
vendored
28
.github/workflows/warn_poetry_updates.yml
vendored
@@ -1,13 +1,13 @@
|
||||
name: Check Poetry Dependencies Changes
|
||||
name: Check uv Dependencies Changes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'poetry.lock'
|
||||
- 'uv.lock'
|
||||
- 'pyproject.toml'
|
||||
|
||||
jobs:
|
||||
check-poetry-changes:
|
||||
check-uv-changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
@@ -17,13 +17,13 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for poetry.lock changes
|
||||
id: check-poetry-lock
|
||||
- name: Check for uv.lock changes
|
||||
id: check-uv-lock
|
||||
run: |
|
||||
if git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | grep -q "poetry.lock"; then
|
||||
echo "poetry_lock_changed=true" >> $GITHUB_OUTPUT
|
||||
if git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | grep -q "uv.lock"; then
|
||||
echo "uv_lock_changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "poetry_lock_changed=false" >> $GITHUB_OUTPUT
|
||||
echo "uv_lock_changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Check for pyproject.toml changes
|
||||
@@ -36,19 +36,19 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Create PR comment
|
||||
if: steps.check-poetry-lock.outputs.poetry_lock_changed == 'true' || steps.check-pyproject.outputs.pyproject_changed == 'true'
|
||||
if: steps.check-uv-lock.outputs.uv_lock_changed == 'true' || steps.check-pyproject.outputs.pyproject_changed == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const poetryLockChanged = ${{ steps.check-poetry-lock.outputs.poetry_lock_changed }};
|
||||
const uvLockChanged = ${{ steps.check-uv-lock.outputs.uv_lock_changed }};
|
||||
const pyprojectChanged = ${{ steps.check-pyproject.outputs.pyproject_changed }};
|
||||
|
||||
let message = '📦 Dependencies Alert:\n\n';
|
||||
|
||||
if (poetryLockChanged && pyprojectChanged) {
|
||||
message += '- Both `poetry.lock` and `pyproject.toml` have been modified\n';
|
||||
} else if (poetryLockChanged) {
|
||||
message += '- `poetry.lock` has been modified\n';
|
||||
if (uvLockChanged && pyprojectChanged) {
|
||||
message += '- Both `uv.lock` and `pyproject.toml` have been modified\n';
|
||||
} else if (uvLockChanged) {
|
||||
message += '- `uv.lock` has been modified\n';
|
||||
} else if (pyprojectChanged) {
|
||||
message += '- `pyproject.toml` has been modified\n';
|
||||
}
|
||||
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -445,31 +445,7 @@ target/
|
||||
|
||||
# IPython
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
|
||||
@@ -13,18 +13,18 @@ repos:
|
||||
hooks:
|
||||
- id: autoflake
|
||||
name: autoflake
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; poetry run autoflake --remove-all-unused-imports --remove-unused-variables --in-place --recursive --ignore-init-module-imports .'
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; uv run autoflake --remove-all-unused-imports --remove-unused-variables --in-place --recursive --ignore-init-module-imports .'
|
||||
language: system
|
||||
types: [python]
|
||||
- id: isort
|
||||
name: isort
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; poetry run isort --profile black .'
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; uv run isort --profile black .'
|
||||
language: system
|
||||
types: [python]
|
||||
exclude: ^docs/
|
||||
- id: black
|
||||
name: black
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; poetry run black --line-length 140 --target-version py310 --target-version py311 .'
|
||||
entry: bash -c '[ -d "apps/core" ] && cd apps/core; uv run black --line-length 140 --target-version py310 --target-version py311 .'
|
||||
language: system
|
||||
types: [python]
|
||||
exclude: ^docs/
|
||||
|
||||
@@ -21,20 +21,20 @@ git clone https://github.com/your-username/letta.git
|
||||
|
||||
### 🧩 Install dependencies & configure environment
|
||||
|
||||
#### Install poetry and dependencies
|
||||
#### Install uv and dependencies
|
||||
|
||||
First, install Poetry using [the official instructions here](https://python-poetry.org/docs/#installation).
|
||||
First, install uv using [the official instructions here](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
|
||||
Once Poetry is installed, navigate to the letta directory and install the Letta project with Poetry:
|
||||
Once uv is installed, navigate to the letta directory and install the Letta project with uv:
|
||||
```shell
|
||||
cd letta
|
||||
eval $(poetry env activate)
|
||||
poetry install --all-extras
|
||||
eval $(uv env activate)
|
||||
uv sync --all-extras
|
||||
```
|
||||
#### Setup PostgreSQL environment (optional)
|
||||
|
||||
If you are planning to develop letta connected to PostgreSQL database, you need to take the following actions.
|
||||
If you are not planning to use PostgreSQL database, you can skip to the step which talks about [running letta](#running-letta-with-poetry).
|
||||
If you are not planning to use PostgreSQL database, you can skip to the step which talks about [running letta](#running-letta-with-uv).
|
||||
|
||||
Assuming you have a running PostgreSQL instance, first you need to create the user, database and ensure the pgvector
|
||||
extension is ready. Here are sample steps for a case where user and database name is letta and assumes no password is set:
|
||||
@@ -50,32 +50,25 @@ export LETTA_PG_URI="postgresql://${POSTGRES_USER:-letta}:${POSTGRES_PASSWORD:-l
|
||||
```
|
||||
|
||||
After this you need to prep the database with initial content. You can use alembic upgrade to populate the initial
|
||||
contents from template test data. Please ensure to activate poetry environment using `poetry shell`.
|
||||
contents from template test data.
|
||||
```shell
|
||||
alembic upgrade head
|
||||
uv run alembic upgrade head
|
||||
```
|
||||
|
||||
#### Running letta with poetry
|
||||
|
||||
Now when you want to use `letta`, make sure you first activate the `poetry` environment using poetry shell:
|
||||
#### Running letta with uv
|
||||
|
||||
Now when you want to use `letta`, you can use `uv run` to run any letta command:
|
||||
```shell
|
||||
$ eval $(poetry env activate)
|
||||
(letta-py3.12) $ letta run
|
||||
```
|
||||
|
||||
Alternatively, you can use `poetry run` (which will activate the `poetry` environment for the `letta run` command only):
|
||||
```shell
|
||||
poetry run letta run
|
||||
uv run letta run
|
||||
```
|
||||
|
||||
#### Installing pre-commit
|
||||
We recommend installing pre-commit to ensure proper formatting during development:
|
||||
```
|
||||
poetry run pre-commit install
|
||||
poetry run pre-commit run --all-files
|
||||
uv run pre-commit install
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
If you don't install pre-commit, you will need to run `poetry run black .` before submitting a PR.
|
||||
If you don't install pre-commit, you will need to run `uv run black .` before submitting a PR.
|
||||
|
||||
## 2. 🛠️ Making Changes
|
||||
|
||||
@@ -95,13 +88,13 @@ Now, the world is your oyster! Go ahead and craft your fabulous changes. 🎨
|
||||
#### Handling Database Migrations
|
||||
If you are running Letta for the first time, your database will be automatically be setup. If you are updating Letta, you may need to run migrations. To run migrations, use the following command:
|
||||
```shell
|
||||
poetry run alembic upgrade head
|
||||
uv run alembic upgrade head
|
||||
```
|
||||
|
||||
#### Creating a new Database Migration
|
||||
If you have made changes to the database models, you will need to create a new migration. To create a new migration, use the following command:
|
||||
```shell
|
||||
poetry run alembic revision --autogenerate -m "Your migration message here"
|
||||
uv run alembic revision --autogenerate -m "Your migration message here"
|
||||
```
|
||||
|
||||
Visit the [Alembic documentation](https://alembic.sqlalchemy.org/en/latest/tutorial.html) for more information on creating and running migrations.
|
||||
@@ -112,9 +105,9 @@ Before we hit the 'Wow, I'm Done' button, let's make sure everything works as ex
|
||||
|
||||
### Run existing tests
|
||||
|
||||
Running tests if you installed via poetry:
|
||||
Running tests:
|
||||
```
|
||||
poetry run pytest -s tests
|
||||
uv run pytest -s tests
|
||||
```
|
||||
|
||||
Running tests if you installed via pip:
|
||||
@@ -126,14 +119,14 @@ pytest -s tests
|
||||
If you added a major feature change, please add new tests in the `tests/` directory.
|
||||
|
||||
## 4. 🧩 Adding new dependencies
|
||||
If you need to add a new dependency to Letta, please add the package via `poetry add <PACKAGE_NAME>`. This will update the `pyproject.toml` and `poetry.lock` files. If the dependency does not need to be installed by all users, make sure to mark the dependency as optional in the `pyproject.toml` file and if needed, create a new extra under `[tool.poetry.extras]`.
|
||||
If you need to add a new dependency to Letta, please add the package via `uv add <PACKAGE_NAME>`. This will update the `pyproject.toml` and `uv.lock` files. If the dependency does not need to be installed by all users, make sure to mark the dependency as optional in the `pyproject.toml` file and if needed, create a new extra under `[project.optional-dependencies]`.
|
||||
|
||||
## 5. 🚀 Submitting Changes
|
||||
|
||||
### Check Formatting
|
||||
Please ensure your code is formatted correctly by running:
|
||||
```
|
||||
poetry run black . -l 140
|
||||
uv run black . -l 140
|
||||
```
|
||||
|
||||
### 🚀 Create a Pull Request
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -14,10 +14,9 @@ RUN apt-get update && apt-get install -y \
|
||||
|
||||
ARG LETTA_ENVIRONMENT=DEV
|
||||
ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} \
|
||||
POETRY_NO_INTERACTION=1 \
|
||||
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
||||
POETRY_VIRTUALENVS_CREATE=1 \
|
||||
POETRY_CACHE_DIR=/tmp/poetry_cache
|
||||
UV_NO_PROGRESS=1 \
|
||||
UV_PYTHON_PREFERENCE=only-managed \
|
||||
UV_CACHE_DIR=/tmp/uv_cache
|
||||
|
||||
# Set for other builds
|
||||
ARG LETTA_VERSION
|
||||
@@ -29,17 +28,16 @@ WORKDIR /app
|
||||
RUN python3 -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Now install poetry in the virtual environment
|
||||
RUN pip install --no-cache-dir poetry==2.1.3
|
||||
# Now install uv in the virtual environment
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
|
||||
# Copy dependency files first
|
||||
COPY pyproject.toml poetry.lock ./
|
||||
COPY apps/core/pyproject.toml apps/core/uv.lock ./
|
||||
# Then copy the rest of the application code
|
||||
COPY . .
|
||||
|
||||
RUN poetry lock && \
|
||||
poetry install --all-extras && \
|
||||
rm -rf $POETRY_CACHE_DIR
|
||||
RUN uv sync --frozen --no-dev --no-install-project --all-extras
|
||||
|
||||
# Runtime stage
|
||||
FROM ankane/pgvector:v0.5.1 AS runtime
|
||||
@@ -48,8 +46,8 @@ FROM ankane/pgvector:v0.5.1 AS runtime
|
||||
ARG NODE_VERSION=22
|
||||
|
||||
RUN apt-get update && \
|
||||
# Install curl and Python
|
||||
apt-get install -y curl python3 python3-venv && \
|
||||
# Install curl, Python, and PostgreSQL client libraries
|
||||
apt-get install -y curl python3 python3-venv libpq-dev && \
|
||||
# Install Node.js
|
||||
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
|
||||
@@ -120,7 +120,7 @@ If your Letta server isn't running on `localhost` (for example, you deployed it
|
||||
|
||||
> _"Do I need to install Docker to use Letta?"_
|
||||
|
||||
No, you can install Letta using `pip` (via `pip install -U letta`), as well as from source (via `poetry install`). See instructions below.
|
||||
No, you can install Letta using `pip` (via `pip install -U letta`), as well as from source (via `uv sync`). See instructions below.
|
||||
|
||||
> _"What's the difference between installing with `pip` vs `Docker`?"_
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ See: https://docs.letta.com/quickstart
|
||||
If you're using Letta Cloud, replace 'baseURL' with 'token'
|
||||
See: https://docs.letta.com/api-reference/overview
|
||||
|
||||
Execute this script using `poetry run python3 example.py`
|
||||
Execute this script using `uv run python3 example.py`
|
||||
|
||||
This will install `letta_client` and other dependencies.
|
||||
"""
|
||||
|
||||
@@ -52,9 +52,9 @@ class JsonSchemaResponseFormat(ResponseFormat):
|
||||
description="The JSON schema of the response.",
|
||||
)
|
||||
|
||||
@field_validator("json_schema")
|
||||
@classmethod
|
||||
def validate_json_schema(cls, v: dict[str, Any]) -> Dict[str, Any]:
|
||||
@field_validator("json_schema")
|
||||
def validate_json_schema(cls, v: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Validate that the provided schema is a valid JSON schema."""
|
||||
if "schema" not in v:
|
||||
raise ValueError("JSON schema should include a schema property")
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/bin/sh
|
||||
echo "Generating OpenAPI schema..."
|
||||
|
||||
# check if poetry is installed
|
||||
if ! command -v poetry &> /dev/null
|
||||
# check if uv is installed
|
||||
if ! command -v uv &> /dev/null
|
||||
then
|
||||
echo "Poetry could not be found. Please install poetry to generate the OpenAPI schema."
|
||||
echo "uv could not be found. Please install uv to generate the OpenAPI schema."
|
||||
exit
|
||||
fi
|
||||
|
||||
# generate OpenAPI schema
|
||||
poetry run python -c 'from letta.server.rest_api.app import app, generate_openapi_schema; generate_openapi_schema(app);'
|
||||
uv run python -c 'from letta.server.rest_api.app import app, generate_openapi_schema; generate_openapi_schema(app);'
|
||||
|
||||
@@ -249,7 +249,7 @@ def compile_system_message(
|
||||
append_icm_if_missing: bool = True,
|
||||
template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
|
||||
previous_message_count: int = 0,
|
||||
archival_memory_size: int = 0,
|
||||
archival_memory_size: int | None = 0,
|
||||
tool_rules_solver: Optional[ToolRulesSolver] = None,
|
||||
sources: Optional[List] = None,
|
||||
max_files_open: Optional[int] = None,
|
||||
@@ -281,7 +281,7 @@ def compile_system_message(
|
||||
memory_metadata_string = PromptGenerator.compile_memory_metadata_block(
|
||||
memory_edit_timestamp=in_context_memory_last_edit,
|
||||
previous_message_count=previous_message_count,
|
||||
archival_memory_size=archival_memory_size,
|
||||
archival_memory_size=archival_memory_size or 0,
|
||||
timezone=timezone,
|
||||
)
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ class LLMBatchManager:
|
||||
query = query.where(LLMBatchJob.organization_id == actor.organization_id)
|
||||
|
||||
if weeks is not None:
|
||||
cutoff_datetime = datetime.datetime.utcnow() - datetime.timedelta(weeks=weeks)
|
||||
cutoff_datetime = datetime.datetime.now(datetime.UTC) - datetime.timedelta(weeks=weeks)
|
||||
query = query.where(LLMBatchJob.created_at >= cutoff_datetime)
|
||||
|
||||
if batch_size is not None:
|
||||
|
||||
@@ -4,7 +4,7 @@ models=("gpt-4-0613" "gpt-3.5-turbo-1106" "gpt-4-1106-preview")
|
||||
## run letta eval
|
||||
for model in "${models[@]}";
|
||||
do
|
||||
poetry run python icml_experiments/doc_qa_task/llm_judge_doc_qa.py --file results/doc_qa_results_model_${model}.json
|
||||
uv run python icml_experiments/doc_qa_task/llm_judge_doc_qa.py --file results/doc_qa_results_model_${model}.json
|
||||
done
|
||||
|
||||
# Iterate over each model
|
||||
@@ -13,6 +13,6 @@ for model in "${models[@]}"; do
|
||||
for doc in "${docs[@]}"; do
|
||||
# Construct and run the command
|
||||
echo "Running for model $model with $doc docs..."
|
||||
poetry run python icml_experiments/doc_qa_task/llm_judge_doc_qa.py --file results/doc_qa_baseline_model_${model}_num_docs_${doc}.json --baseline
|
||||
uv run python icml_experiments/doc_qa_task/llm_judge_doc_qa.py --file results/doc_qa_baseline_model_${model}_num_docs_${doc}.json --baseline
|
||||
done
|
||||
done
|
||||
|
||||
8876
poetry.lock
generated
8876
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
"lock": {
|
||||
"executor": "@nxlv/python:run-commands",
|
||||
"options": {
|
||||
"command": "uv lock --no-update",
|
||||
"command": "uv lock --no-upgrade",
|
||||
"cwd": "apps/core"
|
||||
}
|
||||
},
|
||||
@@ -26,7 +26,10 @@
|
||||
"dev": {
|
||||
"executor": "@nxlv/python:run-commands",
|
||||
"options": {
|
||||
"commands": ["./otel/start-otel-collector.sh", "uv run letta server"],
|
||||
"commands": [
|
||||
"./otel/start-otel-collector.sh",
|
||||
"uv run letta server"
|
||||
],
|
||||
"parallel": true,
|
||||
"cwd": "apps/core"
|
||||
}
|
||||
|
||||
231
pyproject.toml
231
pyproject.toml
@@ -45,7 +45,7 @@ dependencies = [
|
||||
"llama-index>=0.12.2",
|
||||
"llama-index-embeddings-openai>=0.3.1",
|
||||
"anthropic>=0.49.0",
|
||||
"letta_client>=0.1.276",
|
||||
"letta_client>=0.1.277",
|
||||
"openai>=1.99.9",
|
||||
"opentelemetry-api==1.30.0",
|
||||
"opentelemetry-sdk==1.30.0",
|
||||
@@ -57,7 +57,7 @@ dependencies = [
|
||||
"marshmallow-sqlalchemy>=1.4.1",
|
||||
"datamodel-code-generator[http]>=0.25.0",
|
||||
"mcp[cli]>=1.9.4",
|
||||
"firecrawl-py==2.16.5",
|
||||
"firecrawl-py>=2.8.0,<3.0.0",
|
||||
"apscheduler>=3.11.0",
|
||||
"aiomultiprocess>=0.9.1",
|
||||
"matplotlib>=3.10.1",
|
||||
@@ -69,178 +69,90 @@ dependencies = [
|
||||
"orjson>=3.11.1",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
postgres = ["pgvector>=0.2.3", "pg8000>=1.30.3", "psycopg2-binary>=2.9.10", "psycopg2>=2.9.10", "asyncpg>=0.30.0"]
|
||||
redis = ["redis>=6.2.0"]
|
||||
pinecone = ["pinecone[asyncio]>=7.3.0"]
|
||||
dev = ["pytest>=8.0.0", "pytest-asyncio>=0.24.0", "pexpect>=4.9.0", "black>=24.2.0", "pre-commit>=3.5.0", "pyright>=1.1.347", "pytest-order>=1.2.0", "autoflake>=2.3.0", "isort>=5.13.2", "locust>=2.31.5"]
|
||||
experimental = ["uvloop>=0.21.0; sys_platform != 'win32'", "granian[reload]>=2.3.2", "google-cloud-profiler>=4.1.0"]
|
||||
server = ["websockets>=12.0", "fastapi>=0.115.6", "uvicorn>=0.24.0.post1"]
|
||||
cloud-tool-sandbox = ["e2b-code-interpreter==1.5.2", "modal>=1.1.0"]
|
||||
external-tools = ["docker>=7.1.0", "langchain>=0.3.7", "wikipedia>=1.4.0", "langchain-community>=0.3.7", "firecrawl-py==2.16.5"]
|
||||
tests = ["wikipedia>=1.4.0", "pytest-asyncio>=0.24.0"]
|
||||
sqlite = ["aiosqlite>=0.21.0", "sqlite-vec>=0.1.7a2"]
|
||||
bedrock = ["boto3>=1.36.24", "aioboto3>=14.3.0"]
|
||||
google = ["google-genai>=1.15.0"]
|
||||
desktop = ["pyright>=1.1.347", "fastapi>=0.115.6", "uvicorn>=0.24.0.post1", "docker>=7.1.0", "langchain>=0.3.7", "wikipedia>=1.4.0", "langchain-community>=0.3.7", "locust>=2.31.5", "sqlite-vec>=0.1.7a2", "pgvector>=0.2.3"]
|
||||
all = ["pgvector>=0.2.3", "turbopuffer>=0.5.17", "pg8000>=1.30.3", "psycopg2-binary>=2.9.10", "psycopg2>=2.9.10", "pytest", "pytest-asyncio>=0.24.0", "pexpect>=4.9.0", "black>=24.2.0", "pre-commit>=3.5.0", "pyright>=1.1.347", "pytest-order>=1.2.0", "autoflake>=2.3.0", "isort>=5.13.2", "fastapi>=0.115.6", "uvicorn>=0.24.0.post1", "docker>=7.1.0", "langchain>=0.3.7", "wikipedia>=1.4.0", "langchain-community>=0.3.7", "locust>=2.31.5", "uvloop>=0.21.0; sys_platform != 'win32'", "granian[reload]>=2.3.2", "redis>=6.2.0", "pinecone[asyncio]>=7.3.0", "google-cloud-profiler>=4.1.0"]
|
||||
|
||||
[project.scripts]
|
||||
letta = "letta.main:app"
|
||||
|
||||
[tool.poetry]
|
||||
name = "letta"
|
||||
version = "0.10.0"
|
||||
packages = [
|
||||
{include = "letta"},
|
||||
[project.optional-dependencies]
|
||||
# ====== Databases ======
|
||||
postgres = [
|
||||
"pgvector>=0.2.3",
|
||||
"pg8000>=1.30.3",
|
||||
"psycopg2-binary>=2.9.10",
|
||||
"psycopg2>=2.9.10",
|
||||
"asyncpg>=0.30.0",
|
||||
]
|
||||
description = "Create LLM agents with long-term memory and custom tools"
|
||||
authors = [
|
||||
"Letta Team <contact@letta.com>",
|
||||
redis = ["redis>=6.2.0"]
|
||||
pinecone = ["pinecone[asyncio]>=7.3.0"]
|
||||
sqlite = ["aiosqlite>=0.21.0", "sqlite-vec>=0.1.7a2"]
|
||||
|
||||
# ====== Server ======
|
||||
experimental = ["uvloop>=0.21.0", "granian[uvloop,reload]>=2.3.2", "google-cloud-profiler>=4.1.0"]
|
||||
server = [
|
||||
"websockets",
|
||||
"fastapi>=0.115.6",
|
||||
"uvicorn>=0.24.0.post1",
|
||||
]
|
||||
license = "Apache License"
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
letta = "letta.main:app"
|
||||
# ====== LLM Providers ======
|
||||
bedrock = [
|
||||
"boto3>=1.36.24",
|
||||
"aioboto3>=14.3.0",
|
||||
]
|
||||
google = ["google-genai>=1.15.0"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "<3.14,>=3.11"
|
||||
typer = "^0.15.2"
|
||||
questionary = "^2.0.1"
|
||||
pytz = "^2023.3.post1"
|
||||
tqdm = "^4.66.1"
|
||||
black = {extras = ["jupyter"], version = "^24.2.0"}
|
||||
setuptools = "^70"
|
||||
prettytable = "^3.9.0"
|
||||
pgvector = { version = "^0.2.3", optional = true }
|
||||
pre-commit = {version = "^3.5.0", optional = true }
|
||||
pg8000 = {version = "^1.30.3", optional = true}
|
||||
docstring-parser = ">=0.16,<0.17"
|
||||
httpx = "^0.28.0"
|
||||
numpy = "^2.1.0"
|
||||
demjson3 = "^3.0.6"
|
||||
pyyaml = "^6.0.1"
|
||||
sqlalchemy-json = "^0.7.0"
|
||||
fastapi = { version = "^0.115.6", optional = true}
|
||||
uvicorn = {version = "^0.24.0.post1", optional = true}
|
||||
pydantic = "^2.10.6"
|
||||
html2text = "^2020.1.16"
|
||||
sqlalchemy = {extras = ["asyncio"], version = "^2.0.41"}
|
||||
pexpect = {version = "^4.9.0", optional = true}
|
||||
pyright = {version = "^1.1.347", optional = true}
|
||||
#pymilvus = {version ="^2.4.3", optional = true}
|
||||
python-box = "^7.1.1"
|
||||
sqlmodel = "^0.0.16"
|
||||
autoflake = {version = "^2.3.0", optional = true}
|
||||
python-multipart = "^0.0.19"
|
||||
sqlalchemy-utils = "^0.41.2"
|
||||
pytest-order = {version = "^1.2.0", optional = true}
|
||||
pytest-asyncio = {version = "^0.24.0", optional = true}
|
||||
pydantic-settings = "^2.2.1"
|
||||
httpx-sse = "^0.4.0"
|
||||
isort = { version = "^5.13.2", optional = true }
|
||||
docker = {version = "^7.1.0", optional = true}
|
||||
nltk = "^3.8.1"
|
||||
jinja2 = "^3.1.5"
|
||||
locust = {version = "^2.31.5", optional = true}
|
||||
wikipedia = {version = "^1.4.0", optional = true}
|
||||
composio-core = "^0.7.7"
|
||||
alembic = "^1.13.3"
|
||||
pyhumps = "^3.8.0"
|
||||
psycopg2 = {version = "^2.9.10", optional = true}
|
||||
psycopg2-binary = {version = "^2.9.10", optional = true}
|
||||
pathvalidate = "^3.2.1"
|
||||
langchain-community = {version = "^0.3.7", optional = true}
|
||||
langchain = {version = "^0.3.7", optional = true}
|
||||
sentry-sdk = {extras = ["fastapi"], version = "2.19.1"}
|
||||
rich = "^13.9.4"
|
||||
brotli = "^1.1.0"
|
||||
grpcio = "^1.68.1"
|
||||
grpcio-tools = "^1.68.1"
|
||||
llama-index = "^0.12.2"
|
||||
llama-index-embeddings-openai = "^0.3.1"
|
||||
e2b-code-interpreter = {version = "^1.0.3", optional = true}
|
||||
anthropic = "^0.49.0"
|
||||
letta_client = "^0.1.277"
|
||||
openai = "^1.99.9"
|
||||
opentelemetry-api = "1.30.0"
|
||||
opentelemetry-sdk = "1.30.0"
|
||||
opentelemetry-instrumentation-requests = "0.51b0"
|
||||
opentelemetry-instrumentation-sqlalchemy = "0.51b0"
|
||||
opentelemetry-exporter-otlp = "1.30.0"
|
||||
google-genai = {version = "^1.15.0", optional = true}
|
||||
faker = "^36.1.0"
|
||||
colorama = "^0.4.6"
|
||||
marshmallow-sqlalchemy = "^1.4.1"
|
||||
boto3 = {version = "^1.36.24", optional = true}
|
||||
datamodel-code-generator = {extras = ["http"], version = "^0.25.0"}
|
||||
mcp = {extras = ["cli"], version = "^1.9.4"}
|
||||
firecrawl-py = "^2.8.0"
|
||||
apscheduler = "^3.11.0"
|
||||
aiomultiprocess = "^0.9.1"
|
||||
matplotlib = "^3.10.1"
|
||||
asyncpg = {version = "^0.30.0", optional = true}
|
||||
tavily-python = "^0.7.2"
|
||||
mistralai = "^1.8.1"
|
||||
uvloop = {version = "^0.21.0", optional = true, markers = "sys_platform != 'win32'"}
|
||||
granian = {version = "^2.3.2", extras = ["reload"], optional = true}
|
||||
redis = {version = "^6.2.0", optional = true}
|
||||
structlog = "^25.4.0"
|
||||
certifi = "^2025.6.15"
|
||||
aioboto3 = {version = "^14.3.0", optional = true}
|
||||
pinecone = {extras = ["asyncio"], version = "^7.3.0", optional = true}
|
||||
markitdown = {extras = ["docx", "pdf", "pptx"], version = "^0.1.2"}
|
||||
google-cloud-profiler = {version = "^4.1.0", optional = true}
|
||||
sqlite-vec = {version = "^0.1.7a2", optional = true}
|
||||
orjson = "^3.11.1"
|
||||
modal = {version = "^1.1.0", optional = true}
|
||||
turbopuffer = {version = "^0.5.17", optional = true}
|
||||
# ====== Development ======
|
||||
dev = [
|
||||
"pytest",
|
||||
"pytest-asyncio>=0.24.0",
|
||||
"pytest-order>=1.2.0",
|
||||
"pytest-mock>=3.14.0",
|
||||
"pytest-json-report>=1.5.0",
|
||||
"pexpect>=4.9.0",
|
||||
"black[jupyter]>=24.4.2",
|
||||
"pre-commit>=3.5.0",
|
||||
"pyright>=1.1.347",
|
||||
"autoflake>=2.3.0",
|
||||
"isort>=5.13.2",
|
||||
"ipykernel>=6.29.5",
|
||||
"ipdb>=0.13.13",
|
||||
]
|
||||
|
||||
# ====== Other ======
|
||||
cloud-tool-sandbox = ["e2b-code-interpreter>=1.0.3"] # TODO: make this more explicitly e2b
|
||||
modal = ["modal>=1.1.0"]
|
||||
external-tools = [
|
||||
"docker>=7.1.0",
|
||||
"langchain>=0.3.7",
|
||||
"wikipedia>=1.4.0",
|
||||
"langchain-community>=0.3.7",
|
||||
"firecrawl-py>=2.8.0,<3.0.0",
|
||||
"turbopuffer>=0.5.17",
|
||||
]
|
||||
desktop = [
|
||||
"websockets",
|
||||
"fastapi>=0.115.6",
|
||||
"uvicorn>=0.24.0.post1",
|
||||
"docker>=7.1.0",
|
||||
"langchain>=0.3.7",
|
||||
"wikipedia>=1.4.0",
|
||||
"langchain-community>=0.3.7",
|
||||
"locust>=2.31.5",
|
||||
"sqlite-vec>=0.1.7a2",
|
||||
"pgvector>=0.2.3",
|
||||
]
|
||||
|
||||
[tool.poetry.extras]
|
||||
postgres = ["pgvector", "pg8000", "psycopg2-binary", "psycopg2", "asyncpg"]
|
||||
redis = ["redis"]
|
||||
pinecone = ["pinecone"]
|
||||
dev = ["pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "pyright", "pytest-order", "autoflake", "isort", "locust"]
|
||||
experimental = ["uvloop", "granian", "google-cloud-profiler"]
|
||||
server = ["websockets", "fastapi", "uvicorn"]
|
||||
cloud-tool-sandbox = ["e2b-code-interpreter", "modal"] # TODO: split this up
|
||||
external-tools = ["docker", "langchain", "wikipedia", "langchain-community", "firecrawl-py"]
|
||||
tests = ["wikipedia"]
|
||||
bedrock = ["boto3", "aioboto3"]
|
||||
google = ["google-genai"]
|
||||
desktop = ["pyright", "websockets", "fastapi", "uvicorn", "docker", "langchain", "wikipedia", "langchain-community", "locust", "sqlite-vec", "pgvector"]
|
||||
all = ["pgvector", "turbopuffer", "pg8000", "psycopg2-binary", "psycopg2", "pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "pyright", "pytest-order", "autoflake", "isort", "websockets", "fastapi", "uvicorn", "docker", "langchain", "wikipedia", "langchain-community", "locust", "uvloop", "granian", "redis", "pinecone", "google-cloud-profiler"]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^24.4.2"
|
||||
ipykernel = "^6.29.5"
|
||||
ipdb = "^0.13.13"
|
||||
pytest-mock = "^3.14.0"
|
||||
|
||||
[tool.poetry.group."dev,tests".dependencies]
|
||||
pytest-json-report = "^1.5.0"
|
||||
|
||||
|
||||
[tool.poetry.group.sqlite.dependencies]
|
||||
aiosqlite = "^0.21.0"
|
||||
# https://github.com/asg017/sqlite-vec/issues/148
|
||||
sqlite-vec = "^0.1.7a2"
|
||||
|
||||
|
||||
[tool.poetry.group.desktop.dependencies]
|
||||
sqlite-vec = "^0.1.7a2"
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["letta"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 140
|
||||
target-version = ['py310', 'py311', 'py312', 'py313']
|
||||
extend-exclude = "examples/*"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 140
|
||||
@@ -248,3 +160,6 @@ multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
@@ -1033,12 +1033,11 @@ async def test_terminal_tool_rule_send_message_request_heartbeat_false(server, d
|
||||
# Parse the arguments and check request_heartbeat
|
||||
try:
|
||||
arguments = json.loads(send_message_call.tool_call.arguments)
|
||||
assert "request_heartbeat" in arguments, "request_heartbeat should be present in send_message arguments"
|
||||
assert arguments["request_heartbeat"] is False, "request_heartbeat should be False for terminal tool rule"
|
||||
|
||||
print(f"✓ Agent '{agent_name}' correctly set request_heartbeat=False for terminal send_message")
|
||||
except json.JSONDecodeError:
|
||||
pytest.fail("Failed to parse tool call arguments as JSON")
|
||||
|
||||
assert "request_heartbeat" in arguments, "request_heartbeat should be present in send_message arguments"
|
||||
assert arguments["request_heartbeat"] is False, "request_heartbeat should be False for terminal tool rule"
|
||||
|
||||
print(f"✓ Agent '{agent_name}' correctly set request_heartbeat=False for terminal send_message")
|
||||
|
||||
cleanup(server=server, agent_uuid=agent_name, actor=default_user)
|
||||
finally:
|
||||
cleanup(server=server, agent_uuid=agent_name, actor=default_user)
|
||||
|
||||
@@ -759,7 +759,14 @@ def test_tool_call(
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"llm_config",
|
||||
TESTED_LLM_CONFIGS,
|
||||
[
|
||||
(
|
||||
pytest.param(config, marks=pytest.mark.xfail(reason="Qwen image processing unstable - needs investigation"))
|
||||
if config.model == "Qwen/Qwen2.5-72B-Instruct-Turbo"
|
||||
else config
|
||||
)
|
||||
for config in TESTED_LLM_CONFIGS
|
||||
],
|
||||
ids=[c.model for c in TESTED_LLM_CONFIGS],
|
||||
)
|
||||
def test_url_image_input(
|
||||
@@ -797,7 +804,14 @@ def test_url_image_input(
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"llm_config",
|
||||
TESTED_LLM_CONFIGS,
|
||||
[
|
||||
(
|
||||
pytest.param(config, marks=pytest.mark.xfail(reason="Qwen image processing unstable - needs investigation"))
|
||||
if config.model == "Qwen/Qwen2.5-72B-Instruct-Turbo"
|
||||
else config
|
||||
)
|
||||
for config in TESTED_LLM_CONFIGS
|
||||
],
|
||||
ids=[c.model for c in TESTED_LLM_CONFIGS],
|
||||
)
|
||||
def test_base64_image_input(
|
||||
@@ -1568,6 +1582,7 @@ def test_async_greeting_with_callback_url(
|
||||
assert headers.get("Content-Type") == "application/json", "Callback should have JSON content type"
|
||||
|
||||
|
||||
@pytest.mark.flaky(max_runs=2)
|
||||
@pytest.mark.parametrize(
|
||||
"llm_config",
|
||||
TESTED_LLM_CONFIGS,
|
||||
|
||||
@@ -3,6 +3,7 @@ pythonpath = /letta
|
||||
testpaths = /tests
|
||||
asyncio_mode = auto
|
||||
asyncio_default_fixture_loop_scope = function
|
||||
asyncio_default_test_loop_scope = function
|
||||
filterwarnings =
|
||||
ignore::pytest.PytestRemovedIn9Warning
|
||||
# suppresses the warnings we see with the event_loop fixture
|
||||
|
||||
@@ -733,6 +733,7 @@ def validate_id_format(schema: AgentFileSchema) -> bool:
|
||||
class TestFileExport:
|
||||
"""Test file export functionality with comprehensive validation"""
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_basic_file_export(self, default_user, agent_serialization_manager, agent_with_files):
|
||||
"""Test basic file export functionality"""
|
||||
agent_id, source_id, file_id = agent_with_files
|
||||
@@ -755,6 +756,7 @@ class TestFileExport:
|
||||
assert file_agent.file_id == exported.files[0].id
|
||||
assert file_agent.source_id == exported.sources[0].id
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_multiple_files_per_source(self, server, default_user, agent_serialization_manager):
|
||||
"""Test export with multiple files from the same source"""
|
||||
source = await create_test_source(server, "multi-file-source", default_user)
|
||||
@@ -781,6 +783,7 @@ class TestFileExport:
|
||||
assert file_agent.file_id in file_ids
|
||||
assert file_agent.source_id == source_id
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_multiple_sources_export(self, server, default_user, agent_serialization_manager):
|
||||
"""Test export with files from multiple sources"""
|
||||
source1 = await create_test_source(server, "source-1", default_user)
|
||||
@@ -802,6 +805,7 @@ class TestFileExport:
|
||||
for file_schema in exported.files:
|
||||
assert file_schema.source_id in source_ids
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_cross_agent_file_deduplication(self, server, default_user, agent_serialization_manager):
|
||||
"""Test that files shared across agents are deduplicated in export"""
|
||||
source = await create_test_source(server, "shared-source", default_user)
|
||||
@@ -825,6 +829,7 @@ class TestFileExport:
|
||||
assert file_agent.file_id == file_id
|
||||
assert file_agent.source_id == source_id
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_file_agent_relationship_preservation(self, server, default_user, agent_serialization_manager):
|
||||
"""Test that file-agent relationship details are preserved"""
|
||||
source = await create_test_source(server, "test-source", default_user)
|
||||
@@ -841,6 +846,7 @@ class TestFileExport:
|
||||
assert file_agent.is_open is True
|
||||
assert hasattr(file_agent, "last_accessed_at")
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_id_remapping_consistency(self, server, default_user, agent_serialization_manager):
|
||||
"""Test that ID remapping is consistent across all references"""
|
||||
source = await create_test_source(server, "consistency-source", default_user)
|
||||
@@ -859,6 +865,7 @@ class TestFileExport:
|
||||
assert file_agent.file_id == file_schema.id
|
||||
assert file_agent.source_id == source_schema.id
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_empty_file_relationships(self, server, default_user, agent_serialization_manager):
|
||||
"""Test export of agent with no file relationships"""
|
||||
agent_create = CreateAgent(
|
||||
@@ -877,6 +884,7 @@ class TestFileExport:
|
||||
agent_schema = exported.agents[0]
|
||||
assert len(agent_schema.files_agents) == 0
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_file_content_inclusion_in_export(self, default_user, agent_serialization_manager, agent_with_files):
|
||||
"""Test that file content is included in export"""
|
||||
agent_id, source_id, file_id = agent_with_files
|
||||
@@ -985,6 +993,7 @@ class TestAgentFileExport:
|
||||
with pytest.raises(AgentFileExportError): # Should raise AgentFileExportError for non-existent agent
|
||||
await agent_serialization_manager.export(["non-existent-id"], default_user)
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_revision_id_automatic_setting(self, agent_serialization_manager, test_agent, default_user):
|
||||
"""Test that revision_id is automatically set to the latest alembic revision."""
|
||||
agent_file = await agent_serialization_manager.export([test_agent.id], default_user)
|
||||
|
||||
@@ -36,7 +36,7 @@ def swap_letta_config():
|
||||
|
||||
|
||||
def test_letta_run_create_new_agent(swap_letta_config):
|
||||
child = pexpect.spawn("poetry run letta run", encoding="utf-8")
|
||||
child = pexpect.spawn("uv run letta run", encoding="utf-8")
|
||||
# Start the letta run command
|
||||
child.logfile = sys.stdout
|
||||
child.expect("Creating new agent", timeout=20)
|
||||
@@ -79,7 +79,7 @@ def test_letta_run_create_new_agent(swap_letta_config):
|
||||
|
||||
def test_letta_version_prints_only_version(swap_letta_config):
|
||||
# Start the letta version command
|
||||
output = pexpect.run("poetry run letta version", encoding="utf-8")
|
||||
output = pexpect.run("uv run letta version", encoding="utf-8")
|
||||
|
||||
# Remove ANSI escape sequences and whitespace
|
||||
output = re.sub(r"\x1b\[[0-9;]*[mK]", "", output).strip()
|
||||
|
||||
@@ -134,7 +134,7 @@ async def agents(server, weather_tool):
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="function")
|
||||
def batch_requests(agents):
|
||||
"""
|
||||
Create batch requests for each test agent.
|
||||
@@ -151,7 +151,7 @@ def batch_requests(agents):
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="function")
|
||||
def step_state_map(agents):
|
||||
"""
|
||||
Create a mapping of agent IDs to their step states.
|
||||
@@ -264,7 +264,7 @@ def create_failed_response(custom_id: str) -> BetaMessageBatchIndividualResponse
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="function")
|
||||
def dummy_batch_response():
|
||||
"""
|
||||
Create a minimal dummy batch response similar to what Anthropic would return.
|
||||
|
||||
@@ -561,7 +561,6 @@ def server():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.mark.asyncio
|
||||
async def default_archive(server, default_user):
|
||||
archive = await server.archive_manager.create_archive_async("test", actor=default_user)
|
||||
yield archive
|
||||
@@ -700,14 +699,6 @@ def letta_batch_job(server: SyncServer, default_user) -> Job:
|
||||
return server.job_manager.create_job(BatchJob(user_id=default_user.id), actor=default_user)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop(request):
|
||||
"""Create an instance of the default event loop for each test case."""
|
||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def file_attachment(server, default_user, sarah_agent, default_file):
|
||||
assoc, closed_files = await server.file_agent_manager.attach_file(
|
||||
@@ -735,7 +726,6 @@ async def another_file(server, default_source, default_user, default_organizatio
|
||||
# ======================================================================================================================
|
||||
# AgentManager Tests - Basic
|
||||
# ======================================================================================================================
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_agent_exists_async(server: SyncServer, comprehensive_test_agent_fixture, default_user):
|
||||
"""Test the validate_agent_exists_async helper function"""
|
||||
created_agent, _ = comprehensive_test_agent_fixture
|
||||
@@ -1013,9 +1003,8 @@ def set_letta_environment(request):
|
||||
os.environ.pop("LETTA_ENVIRONMENT", None)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_context_window_basic(
|
||||
server: SyncServer, comprehensive_test_agent_fixture, default_user, default_file, event_loop, set_letta_environment
|
||||
server: SyncServer, comprehensive_test_agent_fixture, default_user, default_file, set_letta_environment
|
||||
):
|
||||
# Test agent creation
|
||||
created_agent, create_agent_request = comprehensive_test_agent_fixture
|
||||
@@ -1124,10 +1113,7 @@ async def test_create_agent_with_json_in_system_message(server: SyncServer, defa
|
||||
server.agent_manager.delete_agent(agent_id=agent_state.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_agent(
|
||||
server: SyncServer, comprehensive_test_agent_fixture, other_tool, other_source, other_block, default_user, event_loop
|
||||
):
|
||||
async def test_update_agent(server: SyncServer, comprehensive_test_agent_fixture, other_tool, other_source, other_block, default_user):
|
||||
agent, _ = comprehensive_test_agent_fixture
|
||||
update_agent_request = UpdateAgent(
|
||||
name="train_agent",
|
||||
@@ -1615,21 +1601,18 @@ async def test_bulk_detach_tools_nonexistent_agent(server: SyncServer, print_too
|
||||
await server.agent_manager.bulk_detach_tools_async(agent_id=nonexistent_agent_id, tool_ids=tool_ids, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_tool_nonexistent_agent(server: SyncServer, print_tool, default_user):
|
||||
"""Test attaching a tool to a nonexistent agent."""
|
||||
with pytest.raises(NoResultFound):
|
||||
await server.agent_manager.attach_tool_async(agent_id="nonexistent-agent-id", tool_id=print_tool.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_tool_nonexistent_tool(server: SyncServer, sarah_agent, default_user):
|
||||
"""Test attaching a nonexistent tool to an agent."""
|
||||
with pytest.raises(NoResultFound):
|
||||
await server.agent_manager.attach_tool_async(agent_id=sarah_agent.id, tool_id="nonexistent-tool-id", actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_detach_tool_nonexistent_agent(server: SyncServer, print_tool, default_user):
|
||||
"""Test detaching a tool from a nonexistent agent."""
|
||||
with pytest.raises(NoResultFound):
|
||||
@@ -2023,7 +2006,6 @@ async def test_list_attached_agents(server: SyncServer, sarah_agent, charles_age
|
||||
assert charles_agent.id in [a.id for a in attached_agents]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_attached_agents_nonexistent_source(server: SyncServer, default_user):
|
||||
"""Test listing agents for a nonexistent source."""
|
||||
with pytest.raises(NoResultFound):
|
||||
@@ -2824,10 +2806,7 @@ def mock_embed_model(mock_embeddings):
|
||||
return mock_model
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_list_passages_vector_search(
|
||||
server, default_user, sarah_agent, default_source, default_file, event_loop, mock_embed_model
|
||||
):
|
||||
async def test_agent_list_passages_vector_search(server, default_user, sarah_agent, default_source, default_file, mock_embed_model):
|
||||
"""Test vector search functionality of agent passages"""
|
||||
embed_model = mock_embed_model
|
||||
|
||||
@@ -3053,9 +3032,8 @@ def test_passage_get_by_id(server: SyncServer, agent_passage_fixture, source_pas
|
||||
assert retrieved.text == source_passage_fixture.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_passage_cascade_deletion(
|
||||
server: SyncServer, agent_passage_fixture, source_passage_fixture, default_user, default_source, sarah_agent, event_loop
|
||||
server: SyncServer, agent_passage_fixture, source_passage_fixture, default_user, default_source, sarah_agent
|
||||
):
|
||||
"""Test that passages are deleted when their parent (agent or source) is deleted."""
|
||||
# Verify passages exist
|
||||
@@ -3582,8 +3560,7 @@ async def test_update_user(server: SyncServer):
|
||||
assert user.organization_id == test_org.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_caching(server: SyncServer, event_loop, default_user, performance_pct=0.4):
|
||||
async def test_user_caching(server: SyncServer, default_user, performance_pct=0.4):
|
||||
if isinstance(await get_redis_client(), NoopAsyncRedisClient):
|
||||
pytest.skip("redis not available")
|
||||
# Invalidate previous cache behavior.
|
||||
@@ -3859,7 +3836,6 @@ async def test_upsert_base_tools(server: SyncServer, default_user):
|
||||
assert t.json_schema
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"tool_type,expected_names",
|
||||
[
|
||||
@@ -3886,7 +3862,6 @@ async def test_upsert_filtered_base_tools(server: SyncServer, default_user, tool
|
||||
assert all(t.tool_type == tool_type for t in tools)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upsert_multiple_tool_types(server: SyncServer, default_user):
|
||||
allowed = {ToolType.LETTA_CORE, ToolType.LETTA_BUILTIN, ToolType.LETTA_FILES_CORE}
|
||||
tools = await server.tool_manager.upsert_base_tools_async(actor=default_user, allowed_types=allowed)
|
||||
@@ -3897,13 +3872,11 @@ async def test_upsert_multiple_tool_types(server: SyncServer, default_user):
|
||||
assert all(t.tool_type in allowed for t in tools)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upsert_base_tools_with_empty_type_filter(server: SyncServer, default_user):
|
||||
tools = await server.tool_manager.upsert_base_tools_async(actor=default_user, allowed_types=set())
|
||||
assert tools == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_tools_async(server: SyncServer, default_user):
|
||||
"""Test bulk upserting multiple tools at once"""
|
||||
# create multiple test tools
|
||||
@@ -3960,7 +3933,6 @@ async def test_bulk_upsert_tools_async(server: SyncServer, default_user):
|
||||
assert result[0].description is not None # should be auto-generated from docstring
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_tools_name_conflict(server: SyncServer, default_user):
|
||||
"""Test bulk upserting tools handles name+org_id unique constraint correctly"""
|
||||
|
||||
@@ -4003,7 +3975,6 @@ async def test_bulk_upsert_tools_name_conflict(server: SyncServer, default_user)
|
||||
assert tools_with_name[0].id == original_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_tools_mixed_create_update(server: SyncServer, default_user):
|
||||
"""Test bulk upserting with mix of new tools and updates to existing ones"""
|
||||
|
||||
@@ -4270,13 +4241,11 @@ async def test_create_tool_with_pip_requirements(server: SyncServer, default_use
|
||||
assert created_tool.pip_requirements[1].version is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_tool_without_pip_requirements(server: SyncServer, print_tool):
|
||||
# Verify that tools without pip_requirements have the field as None
|
||||
assert print_tool.pip_requirements is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_tool_pip_requirements(server: SyncServer, print_tool, default_user):
|
||||
# Add pip requirements to existing tool
|
||||
pip_reqs = [
|
||||
@@ -4299,7 +4268,6 @@ async def test_update_tool_pip_requirements(server: SyncServer, print_tool, defa
|
||||
assert updated_tool.pip_requirements[1].version is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_tool_clear_pip_requirements(server: SyncServer, default_user, default_organization):
|
||||
def test_tool_clear_deps():
|
||||
"""
|
||||
@@ -4345,7 +4313,6 @@ async def test_update_tool_clear_pip_requirements(server: SyncServer, default_us
|
||||
assert updated_tool.pip_requirements == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pip_requirements_roundtrip(server: SyncServer, default_user, default_organization):
|
||||
def roundtrip_test_tool():
|
||||
"""
|
||||
@@ -4604,7 +4571,6 @@ def test_create_block(server: SyncServer, default_user):
|
||||
assert block.metadata == block_create.metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_batch_create_blocks_async(server: SyncServer, default_user):
|
||||
"""Test batch creating multiple blocks at once"""
|
||||
block_manager = BlockManager()
|
||||
@@ -4884,10 +4850,7 @@ async def test_batch_create_multiple_blocks(server: SyncServer, default_user):
|
||||
assert expected_labels.issubset(all_labels)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_skips_missing_and_truncates_then_returns_none(
|
||||
server: SyncServer, default_user: PydanticUser, caplog, event_loop
|
||||
):
|
||||
async def test_bulk_update_skips_missing_and_truncates_then_returns_none(server: SyncServer, default_user: PydanticUser, caplog):
|
||||
mgr = BlockManager()
|
||||
|
||||
# create one block with a small limit
|
||||
@@ -4918,7 +4881,6 @@ async def test_bulk_update_skips_missing_and_truncates_then_returns_none(
|
||||
assert reloaded.value == long_val[:5]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(reason="TODO: implement for async")
|
||||
async def test_bulk_update_return_hydrated_true(server: SyncServer, default_user: PydanticUser):
|
||||
mgr = BlockManager()
|
||||
@@ -4938,9 +4900,8 @@ async def test_bulk_update_return_hydrated_true(server: SyncServer, default_user
|
||||
assert updated[0].value == "new-val"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_respects_org_scoping(
|
||||
server: SyncServer, default_user: PydanticUser, other_user_different_org: PydanticUser, caplog, event_loop
|
||||
server: SyncServer, default_user: PydanticUser, other_user_different_org: PydanticUser, caplog
|
||||
):
|
||||
mgr = BlockManager()
|
||||
|
||||
@@ -5587,7 +5548,6 @@ async def test_create_and_upsert_identity(server: SyncServer, default_user):
|
||||
await server.identity_manager.delete_identity_async(identity_id=identity.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_identities(server, default_user):
|
||||
# Create identities to retrieve later
|
||||
user = await server.identity_manager.create_identity_async(
|
||||
@@ -5833,6 +5793,34 @@ async def test_get_set_blocks_for_identities(server: SyncServer, default_block,
|
||||
await server.identity_manager.delete_identity_async(identity_id=identity.id, actor=default_user)
|
||||
|
||||
|
||||
async def test_upsert_properties(server: SyncServer, default_user):
|
||||
identity_create = IdentityCreate(
|
||||
identifier_key="1234",
|
||||
name="caren",
|
||||
identity_type=IdentityType.user,
|
||||
properties=[
|
||||
IdentityProperty(key="email", value="caren@letta.com", type=IdentityPropertyType.string),
|
||||
IdentityProperty(key="age", value=28, type=IdentityPropertyType.number),
|
||||
],
|
||||
)
|
||||
|
||||
identity = await server.identity_manager.create_identity_async(identity_create, actor=default_user)
|
||||
properties = [
|
||||
IdentityProperty(key="email", value="caren@gmail.com", type=IdentityPropertyType.string),
|
||||
IdentityProperty(key="age", value="28", type=IdentityPropertyType.string),
|
||||
IdentityProperty(key="test", value=123, type=IdentityPropertyType.number),
|
||||
]
|
||||
|
||||
updated_identity = await server.identity_manager.upsert_identity_properties_async(
|
||||
identity_id=identity.id,
|
||||
properties=properties,
|
||||
actor=default_user,
|
||||
)
|
||||
assert updated_identity.properties == properties
|
||||
|
||||
await server.identity_manager.delete_identity_async(identity_id=identity.id, actor=default_user)
|
||||
|
||||
|
||||
# ======================================================================================================================
|
||||
# SourceManager Tests - Sources
|
||||
# ======================================================================================================================
|
||||
@@ -5909,7 +5897,6 @@ async def test_create_source(server: SyncServer, default_user):
|
||||
assert source.organization_id == default_user.organization_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_sources_with_same_name_raises_error(server: SyncServer, default_user):
|
||||
"""Test that creating sources with the same name raises an IntegrityError due to unique constraint."""
|
||||
name = "Test Source"
|
||||
@@ -5932,7 +5919,6 @@ async def test_create_sources_with_same_name_raises_error(server: SyncServer, de
|
||||
await server.source_manager.create_source(source=source_pydantic, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_source(server: SyncServer, default_user):
|
||||
"""Test updating an existing source."""
|
||||
source_pydantic = PydanticSource(name="Original Source", description="Original description", embedding_config=DEFAULT_EMBEDDING_CONFIG)
|
||||
@@ -5948,7 +5934,6 @@ async def test_update_source(server: SyncServer, default_user):
|
||||
assert updated_source.metadata == update_data.metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_source(server: SyncServer, default_user):
|
||||
"""Test deleting a source."""
|
||||
source_pydantic = PydanticSource(
|
||||
@@ -5992,7 +5977,6 @@ async def test_delete_attached_source(server: SyncServer, sarah_agent, default_u
|
||||
assert agent is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_sources(server: SyncServer, default_user):
|
||||
"""Test listing sources with pagination."""
|
||||
# Create multiple sources
|
||||
@@ -6019,7 +6003,6 @@ async def test_list_sources(server: SyncServer, default_user):
|
||||
assert next_page[0].name != paginated_sources[0].name
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_source_by_id(server: SyncServer, default_user):
|
||||
"""Test retrieving a source by ID."""
|
||||
source_pydantic = PydanticSource(
|
||||
@@ -6036,7 +6019,6 @@ async def test_get_source_by_id(server: SyncServer, default_user):
|
||||
assert retrieved_source.description == source.description
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_source_by_name(server: SyncServer, default_user):
|
||||
"""Test retrieving a source by name."""
|
||||
source_pydantic = PydanticSource(
|
||||
@@ -6052,7 +6034,6 @@ async def test_get_source_by_name(server: SyncServer, default_user):
|
||||
assert retrieved_source.description == source.description
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_source_no_changes(server: SyncServer, default_user):
|
||||
"""Test update_source with no actual changes to verify logging and response."""
|
||||
source_pydantic = PydanticSource(name="No Change Source", description="No changes", embedding_config=DEFAULT_EMBEDDING_CONFIG)
|
||||
@@ -6068,7 +6049,6 @@ async def test_update_source_no_changes(server: SyncServer, default_user):
|
||||
assert updated_source.description == source.description
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_sources_async(server: SyncServer, default_user):
|
||||
"""Test bulk upserting sources."""
|
||||
sources_data = [
|
||||
@@ -6105,7 +6085,6 @@ async def test_bulk_upsert_sources_async(server: SyncServer, default_user):
|
||||
assert source.organization_id == default_user.organization_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_sources_name_conflict(server: SyncServer, default_user):
|
||||
"""Test bulk upserting sources with name conflicts."""
|
||||
# Create an existing source
|
||||
@@ -6152,7 +6131,6 @@ async def test_bulk_upsert_sources_name_conflict(server: SyncServer, default_use
|
||||
assert new_source.description == "Completely new"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_upsert_sources_mixed_create_update(server: SyncServer, default_user):
|
||||
"""Test bulk upserting with a mix of creates and updates."""
|
||||
# Create some existing sources
|
||||
@@ -6231,7 +6209,6 @@ async def test_bulk_upsert_sources_mixed_create_update(server: SyncServer, defau
|
||||
# ======================================================================================================================
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_by_id(server: SyncServer, default_user, default_source):
|
||||
"""Test retrieving a file by ID."""
|
||||
file_metadata = PydanticFileMetadata(
|
||||
@@ -6253,7 +6230,6 @@ async def test_get_file_by_id(server: SyncServer, default_user, default_source):
|
||||
assert retrieved_file.file_type == created_file.file_type
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_and_retrieve_file_with_content(server, default_user, default_source, async_session):
|
||||
text_body = "Line 1\nLine 2\nLine 3"
|
||||
|
||||
@@ -6282,7 +6258,6 @@ async def test_create_and_retrieve_file_with_content(server, default_user, defau
|
||||
assert loaded.content == text_body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_file_without_content(server, default_user, default_source, async_session):
|
||||
meta = PydanticFileMetadata(
|
||||
file_name="no_body.txt",
|
||||
@@ -6301,7 +6276,6 @@ async def test_create_file_without_content(server, default_user, default_source,
|
||||
assert loaded.content is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lazy_raise_guard(server, default_user, default_source, async_session):
|
||||
text_body = "lazy-raise"
|
||||
|
||||
@@ -6322,13 +6296,11 @@ async def test_lazy_raise_guard(server, default_user, default_source, async_sess
|
||||
await orm.to_pydantic_async(include_content=True)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_files_content_none(server, default_user, default_source):
|
||||
files = await server.file_manager.list_files(source_id=default_source.id, actor=default_user)
|
||||
assert all(f.content is None for f in files)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_cascades_to_content(server, default_user, default_source, async_session):
|
||||
text_body = "to be deleted"
|
||||
meta = PydanticFileMetadata(
|
||||
@@ -6350,7 +6322,6 @@ async def test_delete_cascades_to_content(server, default_user, default_source,
|
||||
assert await _count_file_content_rows(async_session, created.id) == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_by_original_name_and_source_found(server: SyncServer, default_user, default_source):
|
||||
"""Test retrieving a file by original filename and source when it exists."""
|
||||
original_filename = "test_original_file.txt"
|
||||
@@ -6376,7 +6347,6 @@ async def test_get_file_by_original_name_and_source_found(server: SyncServer, de
|
||||
assert retrieved_file.source_id == default_source.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_by_original_name_and_source_not_found(server: SyncServer, default_user, default_source):
|
||||
"""Test retrieving a file by original filename and source when it doesn't exist."""
|
||||
non_existent_filename = "does_not_exist.txt"
|
||||
@@ -6390,7 +6360,6 @@ async def test_get_file_by_original_name_and_source_not_found(server: SyncServer
|
||||
assert retrieved_file is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_by_original_name_and_source_different_sources(server: SyncServer, default_user, default_source):
|
||||
"""Test that files with same original name in different sources are handled correctly."""
|
||||
from letta.schemas.source import Source as PydanticSource
|
||||
@@ -6448,7 +6417,6 @@ async def test_get_file_by_original_name_and_source_different_sources(server: Sy
|
||||
assert retrieved_file_2.source_id == second_source.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_by_original_name_and_source_ignores_deleted(server: SyncServer, default_user, default_source):
|
||||
"""Test that deleted files are ignored when searching by original name and source."""
|
||||
original_filename = "to_be_deleted.txt"
|
||||
@@ -6481,7 +6449,6 @@ async def test_get_file_by_original_name_and_source_ignores_deleted(server: Sync
|
||||
assert retrieved_file_after_delete is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_files(server: SyncServer, default_user, default_source):
|
||||
"""Test listing files with pagination."""
|
||||
# Create multiple files
|
||||
@@ -6510,7 +6477,6 @@ async def test_list_files(server: SyncServer, default_user, default_source):
|
||||
assert next_page[0].file_name != paginated_files[0].file_name
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_file(server: SyncServer, default_user, default_source):
|
||||
"""Test deleting a file."""
|
||||
file_metadata = PydanticFileMetadata(
|
||||
@@ -6529,7 +6495,6 @@ async def test_delete_file(server: SyncServer, default_user, default_source):
|
||||
assert len(files) == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_file_status_basic(server, default_user, default_source):
|
||||
"""Update processing status and error message for a file."""
|
||||
meta = PydanticFileMetadata(
|
||||
@@ -6561,7 +6526,6 @@ async def test_update_file_status_basic(server, default_user, default_source):
|
||||
assert updated.error_message == "Parse failed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_file_status_error_only(server, default_user, default_source):
|
||||
"""Update just the error message, leave status unchanged."""
|
||||
meta = PydanticFileMetadata(
|
||||
@@ -6582,7 +6546,6 @@ async def test_update_file_status_error_only(server, default_user, default_sourc
|
||||
assert updated.processing_status == FileProcessingStatus.PENDING # default from creation
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_file_status_with_chunks(server, default_user, default_source):
|
||||
"""Update chunk progress fields along with status."""
|
||||
meta = PydanticFileMetadata(
|
||||
@@ -7084,7 +7047,6 @@ async def test_same_state_transitions_allowed(server, default_user, default_sour
|
||||
assert updated.total_chunks == 10
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upsert_file_content_basic(server: SyncServer, default_user, default_source, async_session):
|
||||
"""Test creating and updating file content with upsert_file_content()."""
|
||||
initial_text = "Initial content"
|
||||
@@ -7130,7 +7092,6 @@ async def test_upsert_file_content_basic(server: SyncServer, default_user, defau
|
||||
assert orm_file.updated_at >= orm_file.created_at
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_organization_sources_metadata(server, default_user):
|
||||
"""Test getting organization sources metadata with aggregated file information."""
|
||||
# Create test sources
|
||||
@@ -7692,7 +7653,6 @@ async def test_list_jobs_filter_by_type(server: SyncServer, default_user, defaul
|
||||
assert jobs[0].id == run.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_e2e_job_callback(monkeypatch, server: SyncServer, default_user):
|
||||
"""Test that job callbacks are properly dispatched when a job is completed."""
|
||||
captured = {}
|
||||
@@ -8784,9 +8744,8 @@ async def test_update_batch_status(server, default_user, dummy_beta_message_batc
|
||||
assert last_polled_at >= before
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_and_get_batch_item(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -8811,7 +8770,6 @@ async def test_create_and_get_batch_item(
|
||||
assert fetched.id == item.id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_batch_item(
|
||||
server,
|
||||
default_user,
|
||||
@@ -8821,7 +8779,6 @@ async def test_update_batch_item(
|
||||
dummy_step_state,
|
||||
dummy_successful_response,
|
||||
letta_batch_job,
|
||||
event_loop,
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -8855,9 +8812,8 @@ async def test_update_batch_item(
|
||||
assert updated.batch_request_result == dummy_successful_response
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_batch_item(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -8945,7 +8901,6 @@ async def test_bulk_update_batch_statuses(server, default_user, dummy_beta_messa
|
||||
assert updated.latest_polling_response == dummy_beta_message_batch
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_batch_items_results_by_agent(
|
||||
server,
|
||||
default_user,
|
||||
@@ -8955,7 +8910,6 @@ async def test_bulk_update_batch_items_results_by_agent(
|
||||
dummy_step_state,
|
||||
dummy_successful_response,
|
||||
letta_batch_job,
|
||||
event_loop,
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -8980,9 +8934,8 @@ async def test_bulk_update_batch_items_results_by_agent(
|
||||
assert updated.batch_request_result == dummy_successful_response
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_batch_items_step_status_by_agent(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -9006,9 +8959,8 @@ async def test_bulk_update_batch_items_step_status_by_agent(
|
||||
assert updated.step_status == AgentStepStatus.resumed
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_batch_items_limit_and_filter(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -9033,9 +8985,8 @@ async def test_list_batch_items_limit_and_filter(
|
||||
assert len(limited_items) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_batch_items_pagination(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
# Create a batch job.
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
@@ -9098,9 +9049,8 @@ async def test_list_batch_items_pagination(
|
||||
assert empty_page == [], "Expected an empty list when cursor is after the last item"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_batch_items_request_status_by_agent(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
# Create a batch job
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
@@ -9129,14 +9079,12 @@ async def test_bulk_update_batch_items_request_status_by_agent(
|
||||
assert updated.request_status == JobStatus.expired
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_nonexistent_items_should_error(
|
||||
server,
|
||||
default_user,
|
||||
dummy_beta_message_batch,
|
||||
dummy_successful_response,
|
||||
letta_batch_job,
|
||||
event_loop,
|
||||
):
|
||||
# Create a batch job
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
@@ -9172,10 +9120,7 @@ async def test_bulk_update_nonexistent_items_should_error(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_update_nonexistent_items(
|
||||
server, default_user, dummy_beta_message_batch, dummy_successful_response, letta_batch_job, event_loop
|
||||
):
|
||||
async def test_bulk_update_nonexistent_items(server, default_user, dummy_beta_message_batch, dummy_successful_response, letta_batch_job):
|
||||
# Create a batch job
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
llm_provider=ProviderType.anthropic,
|
||||
@@ -9210,9 +9155,8 @@ async def test_bulk_update_nonexistent_items(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_batch_items_bulk(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
# Create a batch job
|
||||
llm_batch_job = await server.batch_manager.create_llm_batch_job_async(
|
||||
@@ -9264,9 +9208,8 @@ async def test_create_batch_items_bulk(
|
||||
assert fetched.id in created_ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_batch_items(
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job, event_loop
|
||||
server, default_user, sarah_agent, dummy_beta_message_batch, dummy_llm_config, dummy_step_state, letta_batch_job
|
||||
):
|
||||
# Create a batch job first.
|
||||
batch = await server.batch_manager.create_llm_batch_job_async(
|
||||
@@ -9606,7 +9549,6 @@ async def test_mcp_server_delete_removes_all_sessions_for_url_and_user(server, d
|
||||
# ======================================================================================================================
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_creates_association(server, default_user, sarah_agent, default_file):
|
||||
assoc, closed_files = await server.file_agent_manager.attach_file(
|
||||
agent_id=sarah_agent.id,
|
||||
@@ -9629,7 +9571,6 @@ async def test_attach_creates_association(server, default_user, sarah_agent, def
|
||||
assert file_blocks[0].label == default_file.file_name
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_is_idempotent(server, default_user, sarah_agent, default_file):
|
||||
a1, closed_files = await server.file_agent_manager.attach_file(
|
||||
agent_id=sarah_agent.id,
|
||||
@@ -9664,7 +9605,6 @@ async def test_attach_is_idempotent(server, default_user, sarah_agent, default_f
|
||||
assert file_blocks[0].label == default_file.file_name
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_file_agent(server, file_attachment, default_user):
|
||||
updated = await server.file_agent_manager.update_file_agent_by_id(
|
||||
agent_id=file_attachment.agent_id,
|
||||
@@ -9677,7 +9617,6 @@ async def test_update_file_agent(server, file_attachment, default_user):
|
||||
assert updated.visible_content == "updated"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_file_agent_by_file_name(server, file_attachment, default_user):
|
||||
updated = await server.file_agent_manager.update_file_agent_by_name(
|
||||
agent_id=file_attachment.agent_id,
|
||||
@@ -9755,7 +9694,6 @@ async def test_file_agent_line_tracking(server, default_user, sarah_agent, defau
|
||||
assert previous_ranges == {file.file_name: (2, 4)} # Should capture the previous range
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_access(server, file_attachment, default_user):
|
||||
old_ts = file_attachment.last_accessed_at
|
||||
if USING_SQLITE:
|
||||
@@ -9776,7 +9714,6 @@ async def test_mark_access(server, file_attachment, default_user):
|
||||
assert refreshed.last_accessed_at > old_ts
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_files_and_agents(
|
||||
server,
|
||||
default_user,
|
||||
@@ -10046,7 +9983,6 @@ async def test_detach_file(server, file_attachment, default_user):
|
||||
assert res is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_detach_file_bulk(
|
||||
server,
|
||||
default_user,
|
||||
@@ -10135,7 +10071,6 @@ async def test_detach_file_bulk(
|
||||
assert deleted_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_org_scoping(
|
||||
server,
|
||||
default_user,
|
||||
@@ -10165,7 +10100,6 @@ async def test_org_scoping(
|
||||
# ======================================================================================================================
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_access_bulk(server, default_user, sarah_agent, default_source):
|
||||
"""Test that mark_access_bulk updates last_accessed_at for multiple files."""
|
||||
import time
|
||||
@@ -10218,7 +10152,6 @@ async def test_mark_access_bulk(server, default_user, sarah_agent, default_sourc
|
||||
assert fa.last_accessed_at == initial_times[file.file_name], f"File {file.file_name} should not have updated timestamp"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lru_eviction_on_attach(server, default_user, sarah_agent, default_source):
|
||||
"""Test that attaching files beyond max_files_open triggers LRU eviction."""
|
||||
import time
|
||||
@@ -10288,7 +10221,6 @@ async def test_lru_eviction_on_attach(server, default_user, sarah_agent, default
|
||||
assert open_file_names == expected_open
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lru_eviction_on_open_file(server, default_user, sarah_agent, default_source):
|
||||
"""Test that opening a file beyond max_files_open triggers LRU eviction."""
|
||||
import time
|
||||
@@ -10377,7 +10309,6 @@ async def test_lru_eviction_on_open_file(server, default_user, sarah_agent, defa
|
||||
assert first_file_agent.is_open is False, "First file should be closed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lru_no_eviction_when_reopening_same_file(server, default_user, sarah_agent, default_source):
|
||||
"""Test that reopening an already open file doesn't trigger unnecessary eviction."""
|
||||
import time
|
||||
@@ -10442,7 +10373,6 @@ async def test_lru_no_eviction_when_reopening_same_file(server, default_user, sa
|
||||
assert initial_open_names == final_open_names, "Same files should remain open"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_last_accessed_at_updates_correctly(server, default_user, sarah_agent, default_source):
|
||||
"""Test that last_accessed_at is updated in the correct scenarios."""
|
||||
import time
|
||||
@@ -10493,7 +10423,6 @@ async def test_last_accessed_at_updates_correctly(server, default_user, sarah_ag
|
||||
assert final_agent.last_accessed_at > prev_time, "mark_access should update timestamp"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_basic(server, default_user, sarah_agent, default_source):
|
||||
"""Test basic functionality of attach_files_bulk method."""
|
||||
# Create multiple files
|
||||
@@ -10538,7 +10467,6 @@ async def test_attach_files_bulk_basic(server, default_user, sarah_agent, defaul
|
||||
assert attached_file.visible_content == f"visible content {i}"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_deduplication(server, default_user, sarah_agent, default_source):
|
||||
"""Test that attach_files_bulk properly deduplicates files with same names."""
|
||||
# Create files with same name (different IDs)
|
||||
@@ -10577,7 +10505,6 @@ async def test_attach_files_bulk_deduplication(server, default_user, sarah_agent
|
||||
assert attached_files[0].file_name == "duplicate_test.txt"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_lru_eviction(server, default_user, sarah_agent, default_source):
|
||||
"""Test that attach_files_bulk properly handles LRU eviction without duplicates."""
|
||||
import time
|
||||
@@ -10657,7 +10584,6 @@ async def test_attach_files_bulk_lru_eviction(server, default_user, sarah_agent,
|
||||
assert f"new_bulk_{i}.txt" in open_file_names
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_mixed_existing_new(server, default_user, sarah_agent, default_source):
|
||||
"""Test bulk attach with mix of existing and new files."""
|
||||
# Create and attach one file individually first
|
||||
@@ -10723,7 +10649,6 @@ async def test_attach_files_bulk_mixed_existing_new(server, default_user, sarah_
|
||||
assert existing_file_agent.visible_content == "updated content"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_empty_list(server, default_user, sarah_agent):
|
||||
"""Test attach_files_bulk with empty file list."""
|
||||
closed_files = await server.file_agent_manager.attach_files_bulk(
|
||||
@@ -10739,7 +10664,6 @@ async def test_attach_files_bulk_empty_list(server, default_user, sarah_agent):
|
||||
assert len(attached_files) == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_attach_files_bulk_oversized_bulk(server, default_user, sarah_agent, default_source):
|
||||
"""Test bulk attach when trying to attach more files than max_files_open allows."""
|
||||
max_files_open = sarah_agent.max_files_open
|
||||
@@ -10793,9 +10717,9 @@ async def test_attach_files_bulk_oversized_bulk(server, default_user, sarah_agen
|
||||
FAILED tests/test_managers.py::test_high_concurrency_stress_test - AssertionError: High concurrency stress test failed with errors: [{'error': "(sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.DeadlockDetectedError'>: deadlock detected\nDETAIL: Process ***04 waits for ShareLock on transaction 30***3; blocked by process 84.\nProcess 84 waits for ShareLock on transaction 30***5; blocked by process ***04.\nHINT: See server log for query details.\n[SQL: INSERT INTO blocks_agents (agent_id, block_id, block_label) VALUES ($***::VARCHAR, $2::VARCHAR, $3::VARCHAR), ($4::VARCHAR, $5::VARCHAR, $6::VARCHAR), ($7::VARCHAR, $8::VARCHAR, $9::VARCHAR), ($***0::VARCHAR, $***::VARCHAR, $***2::VARCHAR) ON CONFLICT DO NOTHING]\n[parameters: ('agent-f69c0ffc-48ea-47f3-a6e0-e26a4***de764d', 'block-4506d355-b84a-44cd-bfdb-63a5039***07f***', 'stress_block_7', 'agent-f69c0ffc-48ea-47f3-a6e0-e26a4***de764d', 'block-cf32229c-9b43-4ed9-b65f-fc7cb***3567bf', 'stress_block_6', 'agent-f69c0ffc-48ea-47f3-a6e0-e26a4***de764d', 'block-02a***8***e7-44d6-402***-85a0-2c3dc20d9fae', 'stress_block_8', 'agent-f69c0ffc-48ea-47f3-a6e0-e26a4***de764d', 'block-4cba5***c***-42b8-4afa-aa59-97022c29f7a2', 'stress_block_0')]\n(Background on this error at: https://sqlalche.me/e/20/dbapi)", 'task_id': 4}]
|
||||
"""
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# @pytest.mark.asyncio(loop_scope="session")
|
||||
# async def test_concurrent_block_updates_race_condition(
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser, event_loop
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser
|
||||
# ):
|
||||
# """Test that concurrent block updates don't cause race conditions."""
|
||||
# agent, _ = comprehensive_test_agent_fixture
|
||||
@@ -10847,9 +10771,9 @@ FAILED tests/test_managers.py::test_high_concurrency_stress_test - AssertionErro
|
||||
# await server.block_manager.delete_block_async(block.id, actor=default_user)
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# @pytest.mark.asyncio(loop_scope="session")
|
||||
# async def test_concurrent_same_block_updates_race_condition(
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser, event_loop
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser
|
||||
# ):
|
||||
# """Test that multiple concurrent updates to the same block configuration don't cause issues."""
|
||||
# agent, _ = comprehensive_test_agent_fixture
|
||||
@@ -10885,9 +10809,9 @@ FAILED tests/test_managers.py::test_high_concurrency_stress_test - AssertionErro
|
||||
# await server.block_manager.delete_block_async(block.id, actor=default_user)
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# @pytest.mark.asyncio(loop_scope="session")
|
||||
# async def test_concurrent_empty_block_updates_race_condition(
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser, event_loop
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser
|
||||
# ):
|
||||
# """Test concurrent updates that remove all blocks."""
|
||||
# agent, _ = comprehensive_test_agent_fixture
|
||||
@@ -10914,9 +10838,9 @@ FAILED tests/test_managers.py::test_high_concurrency_stress_test - AssertionErro
|
||||
# assert len(final_agent.memory.blocks) == 0
|
||||
#
|
||||
#
|
||||
# @pytest.mark.asyncio
|
||||
# @pytest.mark.asyncio(loop_scope="session")
|
||||
# async def test_concurrent_mixed_block_operations_race_condition(
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser, event_loop
|
||||
# server: SyncServer, comprehensive_test_agent_fixture, default_user: PydanticUser
|
||||
# ):
|
||||
# """Test mixed concurrent operations: some adding blocks, some removing."""
|
||||
# agent, _ = comprehensive_test_agent_fixture
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from letta.config import LettaConfig
|
||||
@@ -15,41 +13,9 @@ from letta.schemas.group import (
|
||||
SupervisorManager,
|
||||
)
|
||||
from letta.schemas.message import MessageCreate
|
||||
from letta.server.db import db_registry
|
||||
from letta.server.server import SyncServer
|
||||
|
||||
|
||||
# Disable SQLAlchemy connection pooling for tests to prevent event loop issues
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def disable_db_pooling_for_tests():
|
||||
"""Disable database connection pooling for the entire test session."""
|
||||
os.environ["LETTA_DISABLE_SQLALCHEMY_POOLING"] = "true"
|
||||
yield
|
||||
# Clean up environment variable after tests
|
||||
if "LETTA_DISABLE_SQLALCHEMY_POOLING" in os.environ:
|
||||
del os.environ["LETTA_DISABLE_SQLALCHEMY_POOLING"]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def cleanup_db_connections():
|
||||
"""Cleanup database connections after each test."""
|
||||
yield
|
||||
|
||||
# Dispose async engines in the current event loop
|
||||
try:
|
||||
if hasattr(db_registry, "_async_engines"):
|
||||
for engine in db_registry._async_engines.values():
|
||||
if engine:
|
||||
await engine.dispose()
|
||||
# Reset async initialization to force fresh connections
|
||||
db_registry._initialized["async"] = False
|
||||
db_registry._async_engines.clear()
|
||||
db_registry._async_session_factories.clear()
|
||||
except Exception as e:
|
||||
# Log the error but don't fail the test
|
||||
print(f"Warning: Failed to cleanup database connections: {e}")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def server():
|
||||
config = LettaConfig.load()
|
||||
@@ -157,7 +123,6 @@ async def manager_agent(server, default_user):
|
||||
yield agent_scooby
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_group(server, default_user):
|
||||
group = await server.group_manager.create_group_async(
|
||||
group=GroupCreate(
|
||||
@@ -182,7 +147,6 @@ async def test_empty_group(server, default_user):
|
||||
await server.group_manager.delete_group_async(group_id=group.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_modify_group_pattern(server, default_user, four_participant_agents, manager_agent):
|
||||
group = await server.group_manager.create_group_async(
|
||||
group=GroupCreate(
|
||||
@@ -206,7 +170,6 @@ async def test_modify_group_pattern(server, default_user, four_participant_agent
|
||||
await server.group_manager.delete_group_async(group_id=group.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_agent_groups(server, default_user, four_participant_agents):
|
||||
group_a = await server.group_manager.create_group_async(
|
||||
group=GroupCreate(
|
||||
@@ -232,7 +195,6 @@ async def test_list_agent_groups(server, default_user, four_participant_agents):
|
||||
await server.group_manager.delete_group_async(group_id=group_b.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_round_robin(server, default_user, four_participant_agents):
|
||||
description = (
|
||||
"This is a group chat between best friends all like to hang out together. In their free time they like to solve mysteries."
|
||||
@@ -344,7 +306,6 @@ async def test_round_robin(server, default_user, four_participant_agents):
|
||||
await server.group_manager.delete_group_async(group_id=group.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_supervisor(server, default_user, four_participant_agents):
|
||||
agent_scrappy = await server.create_agent_async(
|
||||
request=CreateAgent(
|
||||
@@ -408,7 +369,6 @@ async def test_supervisor(server, default_user, four_participant_agents):
|
||||
server.agent_manager.delete_agent(agent_id=agent_scrappy.id, actor=default_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.flaky(max_runs=2)
|
||||
async def test_dynamic_group_chat(server, default_user, manager_agent, four_participant_agents):
|
||||
description = (
|
||||
|
||||
@@ -198,6 +198,7 @@ async def test_vllm():
|
||||
# assert embedding_models[0].handle == f"{provider.name}/{embedding_models[0].embedding_model}"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_custom_anthropic():
|
||||
provider = AnthropicProvider(
|
||||
name="custom_anthropic",
|
||||
|
||||
@@ -23,6 +23,12 @@ from tests.helpers.utils import upload_file_and_wait
|
||||
SERVER_PORT = 8283
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
"""Override asyncio settings for this test file"""
|
||||
# config.option.asyncio_default_fixture_loop_scope = "function"
|
||||
config.option.asyncio_default_test_loop_scope = "function"
|
||||
|
||||
|
||||
def run_server():
|
||||
load_dotenv()
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
@@ -28,239 +27,6 @@ from letta.schemas.message import Message
|
||||
from letta.server.server import SyncServer
|
||||
from letta.system import unpack_message
|
||||
|
||||
WAR_AND_PEACE = """BOOK ONE: 1805
|
||||
|
||||
CHAPTER I
|
||||
|
||||
“Well, Prince, so Genoa and Lucca are now just family estates of the
|
||||
Buonapartes. But I warn you, if you don't tell me that this means war,
|
||||
if you still try to defend the infamies and horrors perpetrated by that
|
||||
Antichrist—I really believe he is Antichrist—I will have nothing
|
||||
more to do with you and you are no longer my friend, no longer my
|
||||
'faithful slave,' as you call yourself! But how do you do? I see I
|
||||
have frightened you—sit down and tell me all the news.”
|
||||
|
||||
It was in July, 1805, and the speaker was the well-known Anna Pávlovna
|
||||
Schérer, maid of honor and favorite of the Empress Márya Fëdorovna.
|
||||
With these words she greeted Prince Vasíli Kurágin, a man of high
|
||||
rank and importance, who was the first to arrive at her reception. Anna
|
||||
Pávlovna had had a cough for some days. She was, as she said, suffering
|
||||
from la grippe; grippe being then a new word in St. Petersburg, used
|
||||
only by the elite.
|
||||
|
||||
All her invitations without exception, written in French, and delivered
|
||||
by a scarlet-liveried footman that morning, ran as follows:
|
||||
|
||||
“If you have nothing better to do, Count (or Prince), and if the
|
||||
prospect of spending an evening with a poor invalid is not too terrible,
|
||||
I shall be very charmed to see you tonight between 7 and 10—Annette
|
||||
Schérer.”
|
||||
|
||||
“Heavens! what a virulent attack!” replied the prince, not in the
|
||||
least disconcerted by this reception. He had just entered, wearing an
|
||||
embroidered court uniform, knee breeches, and shoes, and had stars on
|
||||
his breast and a serene expression on his flat face. He spoke in that
|
||||
refined French in which our grandfathers not only spoke but thought, and
|
||||
with the gentle, patronizing intonation natural to a man of importance
|
||||
who had grown old in society and at court. He went up to Anna Pávlovna,
|
||||
kissed her hand, presenting to her his bald, scented, and shining head,
|
||||
and complacently seated himself on the sofa.
|
||||
|
||||
“First of all, dear friend, tell me how you are. Set your friend's
|
||||
mind at rest,” said he without altering his tone, beneath the
|
||||
politeness and affected sympathy of which indifference and even irony
|
||||
could be discerned.
|
||||
|
||||
“Can one be well while suffering morally? Can one be calm in times
|
||||
like these if one has any feeling?” said Anna Pávlovna. “You are
|
||||
staying the whole evening, I hope?”
|
||||
|
||||
“And the fete at the English ambassador's? Today is Wednesday. I
|
||||
must put in an appearance there,” said the prince. “My daughter is
|
||||
coming for me to take me there.”
|
||||
|
||||
“I thought today's fete had been canceled. I confess all these
|
||||
festivities and fireworks are becoming wearisome.”
|
||||
|
||||
“If they had known that you wished it, the entertainment would have
|
||||
been put off,” said the prince, who, like a wound-up clock, by force
|
||||
of habit said things he did not even wish to be believed.
|
||||
|
||||
“Don't tease! Well, and what has been decided about Novosíltsev's
|
||||
dispatch? You know everything.”
|
||||
|
||||
“What can one say about it?” replied the prince in a cold, listless
|
||||
tone. “What has been decided? They have decided that Buonaparte has
|
||||
burnt his boats, and I believe that we are ready to burn ours.”
|
||||
|
||||
Prince Vasíli always spoke languidly, like an actor repeating a stale
|
||||
part. Anna Pávlovna Schérer on the contrary, despite her forty years,
|
||||
overflowed with animation and impulsiveness. To be an enthusiast had
|
||||
become her social vocation and, sometimes even when she did not
|
||||
feel like it, she became enthusiastic in order not to disappoint the
|
||||
expectations of those who knew her. The subdued smile which, though it
|
||||
did not suit her faded features, always played round her lips expressed,
|
||||
as in a spoiled child, a continual consciousness of her charming defect,
|
||||
which she neither wished, nor could, nor considered it necessary, to
|
||||
correct.
|
||||
|
||||
In the midst of a conversation on political matters Anna Pávlovna burst
|
||||
out:
|
||||
|
||||
“Oh, don't speak to me of Austria. Perhaps I don't understand
|
||||
things, but Austria never has wished, and does not wish, for war. She
|
||||
is betraying us! Russia alone must save Europe. Our gracious sovereign
|
||||
recognizes his high vocation and will be true to it. That is the one
|
||||
thing I have faith in! Our good and wonderful sovereign has to perform
|
||||
the noblest role on earth, and he is so virtuous and noble that God will
|
||||
not forsake him. He will fulfill his vocation and crush the hydra of
|
||||
revolution, which has become more terrible than ever in the person of
|
||||
this murderer and villain! We alone must avenge the blood of the just
|
||||
one.... Whom, I ask you, can we rely on?... England with her commercial
|
||||
spirit will not and cannot understand the Emperor Alexander's
|
||||
loftiness of soul. She has refused to evacuate Malta. She wanted to
|
||||
find, and still seeks, some secret motive in our actions. What answer
|
||||
did Novosíltsev get? None. The English have not understood and cannot
|
||||
understand the self-abnegation of our Emperor who wants nothing for
|
||||
himself, but only desires the good of mankind. And what have they
|
||||
promised? Nothing! And what little they have promised they will not
|
||||
perform! Prussia has always declared that Buonaparte is invincible, and
|
||||
that all Europe is powerless before him.... And I don't believe a
|
||||
word that Hardenburg says, or Haugwitz either. This famous Prussian
|
||||
neutrality is just a trap. I have faith only in God and the lofty
|
||||
destiny of our adored monarch. He will save Europe!”
|
||||
|
||||
She suddenly paused, smiling at her own impetuosity.
|
||||
|
||||
“I think,” said the prince with a smile, “that if you had been
|
||||
sent instead of our dear Wintzingerode you would have captured the King
|
||||
of Prussia's consent by assault. You are so eloquent. Will you give me
|
||||
a cup of tea?”
|
||||
|
||||
“In a moment. À propos,” she added, becoming calm again, “I am
|
||||
expecting two very interesting men tonight, le Vicomte de Mortemart, who
|
||||
is connected with the Montmorencys through the Rohans, one of the best
|
||||
French families. He is one of the genuine émigrés, the good ones. And
|
||||
also the Abbé Morio. Do you know that profound thinker? He has been
|
||||
received by the Emperor. Had you heard?”
|
||||
|
||||
“I shall be delighted to meet them,” said the prince. “But
|
||||
tell me,” he added with studied carelessness as if it had only just
|
||||
occurred to him, though the question he was about to ask was the chief
|
||||
motive of his visit, “is it true that the Dowager Empress wants
|
||||
Baron Funke to be appointed first secretary at Vienna? The baron by all
|
||||
accounts is a poor creature.”
|
||||
|
||||
Prince Vasíli wished to obtain this post for his son, but others were
|
||||
trying through the Dowager Empress Márya Fëdorovna to secure it for
|
||||
the baron.
|
||||
|
||||
Anna Pávlovna almost closed her eyes to indicate that neither she nor
|
||||
anyone else had a right to criticize what the Empress desired or was
|
||||
pleased with.
|
||||
|
||||
“Baron Funke has been recommended to the Dowager Empress by her
|
||||
sister,” was all she said, in a dry and mournful tone.
|
||||
|
||||
As she named the Empress, Anna Pávlovna's face suddenly assumed an
|
||||
expression of profound and sincere devotion and respect mingled with
|
||||
sadness, and this occurred every time she mentioned her illustrious
|
||||
patroness. She added that Her Majesty had deigned to show Baron Funke
|
||||
beaucoup d'estime, and again her face clouded over with sadness.
|
||||
|
||||
The prince was silent and looked indifferent. But, with the womanly and
|
||||
courtierlike quickness and tact habitual to her, Anna Pávlovna
|
||||
wished both to rebuke him (for daring to speak as he had done of a man
|
||||
recommended to the Empress) and at the same time to console him, so she
|
||||
said:
|
||||
|
||||
“Now about your family. Do you know that since your daughter came
|
||||
out everyone has been enraptured by her? They say she is amazingly
|
||||
beautiful.”
|
||||
|
||||
The prince bowed to signify his respect and gratitude.
|
||||
|
||||
“I often think,” she continued after a short pause, drawing nearer
|
||||
to the prince and smiling amiably at him as if to show that political
|
||||
and social topics were ended and the time had come for intimate
|
||||
conversation—“I often think how unfairly sometimes the joys of life
|
||||
are distributed. Why has fate given you two such splendid children?
|
||||
I don't speak of Anatole, your youngest. I don't like him,” she
|
||||
added in a tone admitting of no rejoinder and raising her eyebrows.
|
||||
“Two such charming children. And really you appreciate them less than
|
||||
anyone, and so you don't deserve to have them.”
|
||||
|
||||
And she smiled her ecstatic smile.
|
||||
|
||||
“I can't help it,” said the prince. “Lavater would have said I
|
||||
lack the bump of paternity.”
|
||||
|
||||
“Don't joke; I mean to have a serious talk with you. Do you know
|
||||
I am dissatisfied with your younger son? Between ourselves” (and her
|
||||
face assumed its melancholy expression), “he was mentioned at Her
|
||||
Majesty's and you were pitied....”
|
||||
|
||||
The prince answered nothing, but she looked at him significantly,
|
||||
awaiting a reply. He frowned.
|
||||
|
||||
“What would you have me do?” he said at last. “You know I did all
|
||||
a father could for their education, and they have both turned out fools.
|
||||
Hippolyte is at least a quiet fool, but Anatole is an active one. That
|
||||
is the only difference between them.” He said this smiling in a way
|
||||
more natural and animated than usual, so that the wrinkles round
|
||||
his mouth very clearly revealed something unexpectedly coarse and
|
||||
unpleasant.
|
||||
|
||||
“And why are children born to such men as you? If you were not a
|
||||
father there would be nothing I could reproach you with,” said Anna
|
||||
Pávlovna, looking up pensively.
|
||||
|
||||
“I am your faithful slave and to you alone I can confess that my
|
||||
children are the bane of my life. It is the cross I have to bear. That
|
||||
is how I explain it to myself. It can't be helped!”
|
||||
|
||||
He said no more, but expressed his resignation to cruel fate by a
|
||||
gesture. Anna Pávlovna meditated.
|
||||
|
||||
“Have you never thought of marrying your prodigal son Anatole?” she
|
||||
asked. “They say old maids have a mania for matchmaking, and though I
|
||||
don't feel that weakness in myself as yet, I know a little person who
|
||||
is very unhappy with her father. She is a relation of yours, Princess
|
||||
Mary Bolkónskaya.”
|
||||
|
||||
Prince Vasíli did not reply, though, with the quickness of memory and
|
||||
perception befitting a man of the world, he indicated by a movement of
|
||||
the head that he was considering this information.
|
||||
|
||||
“Do you know,” he said at last, evidently unable to check the sad
|
||||
current of his thoughts, “that Anatole is costing me forty thousand
|
||||
rubles a year? And,” he went on after a pause, “what will it be in
|
||||
five years, if he goes on like this?” Presently he added: “That's
|
||||
what we fathers have to put up with.... Is this princess of yours
|
||||
rich?”
|
||||
|
||||
“Her father is very rich and stingy. He lives in the country. He is
|
||||
the well-known Prince Bolkónski who had to retire from the army under
|
||||
the late Emperor, and was nicknamed 'the King of Prussia.' He is
|
||||
very clever but eccentric, and a bore. The poor girl is very unhappy.
|
||||
She has a brother; I think you know him, he married Lise Meinen lately.
|
||||
He is an aide-de-camp of Kutúzov's and will be here tonight.”
|
||||
|
||||
“Listen, dear Annette,” said the prince, suddenly taking Anna
|
||||
Pávlovna's hand and for some reason drawing it downwards. “Arrange
|
||||
that affair for me and I shall always be your most devoted slave-slafe
|
||||
with an f, as a village elder of mine writes in his reports. She is rich
|
||||
and of good family and that's all I want.”
|
||||
|
||||
And with the familiarity and easy grace peculiar to him, he raised the
|
||||
maid of honor's hand to his lips, kissed it, and swung it to and fro
|
||||
as he lay back in his armchair, looking in another direction.
|
||||
|
||||
“Attendez,” said Anna Pávlovna, reflecting, “I'll speak to
|
||||
Lise, young Bolkónski's wife, this very evening, and perhaps the
|
||||
thing can be arranged. It shall be on your family's behalf that I'll
|
||||
start my apprenticeship as old maid."""
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def server():
|
||||
@@ -359,14 +125,6 @@ def other_agent_id(server, user_id, base_tools):
|
||||
server.agent_manager.delete_agent(agent_state.id, actor=actor)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop(request):
|
||||
"""Create an instance of the default event loop for each test case."""
|
||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
def test_error_on_nonexistent_agent(server, user, agent_id):
|
||||
try:
|
||||
fake_agent_id = str(uuid.uuid4())
|
||||
@@ -456,18 +214,21 @@ async def test_get_context_window_overview(server: SyncServer, user, agent_id):
|
||||
assert overview.messages is not None
|
||||
|
||||
assert overview.context_window_size_max >= overview.context_window_size_current
|
||||
assert overview.context_window_size_current == (
|
||||
overview.num_tokens_system
|
||||
+ overview.num_tokens_core_memory
|
||||
+ overview.num_tokens_summary_memory
|
||||
+ overview.num_tokens_messages
|
||||
+ overview.num_tokens_functions_definitions
|
||||
+ overview.num_tokens_external_memory_summary
|
||||
assert overview.context_window_size_current == sum(
|
||||
(
|
||||
overview.num_tokens_system,
|
||||
overview.num_tokens_core_memory,
|
||||
overview.num_tokens_summary_memory,
|
||||
overview.num_tokens_messages,
|
||||
overview.num_tokens_functions_definitions,
|
||||
overview.num_tokens_external_memory_summary,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_delete_agent_same_org(server: SyncServer, org_id: str, user: User):
|
||||
agent_state = server.create_agent(
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_delete_agent_same_org(server: SyncServer, org_id: str, user: User):
|
||||
agent_state = await server.create_agent_async(
|
||||
request=CreateAgent(
|
||||
name="nonexistent_tools_agent",
|
||||
memory_blocks=[],
|
||||
@@ -478,10 +239,10 @@ def test_delete_agent_same_org(server: SyncServer, org_id: str, user: User):
|
||||
)
|
||||
|
||||
# create another user in the same org
|
||||
another_user = server.user_manager.create_user(User(organization_id=org_id, name="another"))
|
||||
another_user = await server.user_manager.create_actor_async(User(organization_id=org_id, name="another"))
|
||||
|
||||
# test that another user in the same org can delete the agent
|
||||
server.agent_manager.delete_agent(agent_state.id, actor=another_user)
|
||||
await server.agent_manager.delete_agent_async(agent_state.id, actor=another_user)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -720,7 +481,7 @@ def ingest(message: str):
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_basic(server, disable_e2b_api_key, user):
|
||||
"""Test running a simple tool from source"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -735,7 +496,7 @@ async def test_tool_run_basic(server, disable_e2b_api_key, user):
|
||||
assert not result.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_with_env_var(server, disable_e2b_api_key, user):
|
||||
"""Test running a tool that uses an environment variable"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -751,7 +512,7 @@ async def test_tool_run_with_env_var(server, disable_e2b_api_key, user):
|
||||
assert not result.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_invalid_args(server, disable_e2b_api_key, user):
|
||||
"""Test running a tool with incorrect arguments"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -768,7 +529,7 @@ async def test_tool_run_invalid_args(server, disable_e2b_api_key, user):
|
||||
assert "missing 1 required positional argument" in result.stderr[0]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_with_distractor(server, disable_e2b_api_key, user):
|
||||
"""Test running a tool with a distractor function in the source"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -784,7 +545,7 @@ async def test_tool_run_with_distractor(server, disable_e2b_api_key, user):
|
||||
assert not result.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(scope="session")
|
||||
async def test_tool_run_explicit_tool_name(server, disable_e2b_api_key, user):
|
||||
"""Test selecting a tool by name when multiple tools exist in the source"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -801,7 +562,7 @@ async def test_tool_run_explicit_tool_name(server, disable_e2b_api_key, user):
|
||||
assert not result.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_util_function(server, disable_e2b_api_key, user):
|
||||
"""Test selecting a utility function that does not return anything meaningful"""
|
||||
result = await server.run_tool_from_source(
|
||||
@@ -818,7 +579,7 @@ async def test_tool_run_util_function(server, disable_e2b_api_key, user):
|
||||
assert not result.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_tool_run_with_explicit_json_schema(server, disable_e2b_api_key, user):
|
||||
"""Test overriding the autogenerated JSON schema with an explicit one"""
|
||||
explicit_json_schema = {
|
||||
@@ -941,13 +702,13 @@ def test_default_tool_rules(server: SyncServer, user_id: str, base_tools, base_m
|
||||
assert len(agent_state.tool_rules) == len(base_tools + base_memory_tools)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_add_remove_tools_update_agent(server: SyncServer, user_id: str, base_tools, base_memory_tools):
|
||||
"""Test that the memory rebuild is generating the correct number of role=system messages"""
|
||||
actor = server.user_manager.get_user_or_default(user_id)
|
||||
|
||||
# create agent
|
||||
agent_state = server.create_agent(
|
||||
agent_state = await server.create_agent_async(
|
||||
request=CreateAgent(
|
||||
name="memory_rebuild_test_agent",
|
||||
tool_ids=[],
|
||||
|
||||
529
uv.lock
generated
529
uv.lock
generated
@@ -239,6 +239,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "appnope"
|
||||
version = "0.1.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apscheduler"
|
||||
version = "3.11.0"
|
||||
@@ -713,6 +722,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "comm"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-core"
|
||||
version = "0.7.20"
|
||||
@@ -900,6 +918,27 @@ http = [
|
||||
{ name = "httpx" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugpy"
|
||||
version = "1.8.16"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/d4/722d0bcc7986172ac2ef3c979ad56a1030e3afd44ced136d45f8142b1f4a/debugpy-1.8.16.tar.gz", hash = "sha256:31e69a1feb1cf6b51efbed3f6c9b0ef03bc46ff050679c4be7ea6d2e23540870", size = 1643809, upload-time = "2025-08-06T18:00:02.647Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/63/d6/ad70ba8b49b23fa286fb21081cf732232cc19374af362051da9c7537ae52/debugpy-1.8.16-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67371b28b79a6a12bcc027d94a06158f2fde223e35b5c4e0783b6f9d3b39274a", size = 2184063, upload-time = "2025-08-06T18:00:11.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/49/7b03e88dea9759a4c7910143f87f92beb494daaae25560184ff4ae883f9e/debugpy-1.8.16-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2abae6dd02523bec2dee16bd6b0781cccb53fd4995e5c71cc659b5f45581898", size = 3134837, upload-time = "2025-08-06T18:00:13.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/52/b348930316921de7565fbe37a487d15409041713004f3d74d03eb077dbd4/debugpy-1.8.16-cp311-cp311-win32.whl", hash = "sha256:f8340a3ac2ed4f5da59e064aa92e39edd52729a88fbde7bbaa54e08249a04493", size = 5159142, upload-time = "2025-08-06T18:00:15.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/ef/9aa9549ce1e10cea696d980292e71672a91ee4a6a691ce5f8629e8f48c49/debugpy-1.8.16-cp311-cp311-win_amd64.whl", hash = "sha256:70f5fcd6d4d0c150a878d2aa37391c52de788c3dc680b97bdb5e529cb80df87a", size = 5183117, upload-time = "2025-08-06T18:00:17.251Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/fb/0387c0e108d842c902801bc65ccc53e5b91d8c169702a9bbf4f7efcedf0c/debugpy-1.8.16-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:b202e2843e32e80b3b584bcebfe0e65e0392920dc70df11b2bfe1afcb7a085e4", size = 2511822, upload-time = "2025-08-06T18:00:18.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/44/19e02745cae22bf96440141f94e15a69a1afaa3a64ddfc38004668fcdebf/debugpy-1.8.16-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64473c4a306ba11a99fe0bb14622ba4fbd943eb004847d9b69b107bde45aa9ea", size = 4230135, upload-time = "2025-08-06T18:00:19.997Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/0b/19b1ba5ee4412f303475a2c7ad5858efb99c90eae5ec627aa6275c439957/debugpy-1.8.16-cp312-cp312-win32.whl", hash = "sha256:833a61ed446426e38b0dd8be3e9d45ae285d424f5bf6cd5b2b559c8f12305508", size = 5281271, upload-time = "2025-08-06T18:00:21.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/e0/bc62e2dc141de53bd03e2c7cb9d7011de2e65e8bdcdaa26703e4d28656ba/debugpy-1.8.16-cp312-cp312-win_amd64.whl", hash = "sha256:75f204684581e9ef3dc2f67687c3c8c183fde2d6675ab131d94084baf8084121", size = 5323149, upload-time = "2025-08-06T18:00:23.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/66/607ab45cc79e60624df386e233ab64a6d8d39ea02e7f80e19c1d451345bb/debugpy-1.8.16-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:85df3adb1de5258dca910ae0bb185e48c98801ec15018a263a92bb06be1c8787", size = 2496157, upload-time = "2025-08-06T18:00:24.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/a0/c95baae08a75bceabb79868d663a0736655e427ab9c81fb848da29edaeac/debugpy-1.8.16-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee89e948bc236a5c43c4214ac62d28b29388453f5fd328d739035e205365f0b", size = 4222491, upload-time = "2025-08-06T18:00:25.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/2f/1c8db6ddd8a257c3cd2c46413b267f1d5fa3df910401c899513ce30392d6/debugpy-1.8.16-cp313-cp313-win32.whl", hash = "sha256:cf358066650439847ec5ff3dae1da98b5461ea5da0173d93d5e10f477c94609a", size = 5281126, upload-time = "2025-08-06T18:00:27.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/ba/c3e154ab307366d6c5a9c1b68de04914e2ce7fa2f50d578311d8cc5074b2/debugpy-1.8.16-cp313-cp313-win_amd64.whl", hash = "sha256:b5aea1083f6f50023e8509399d7dc6535a351cc9f2e8827d1e093175e4d9fa4c", size = 5323094, upload-time = "2025-08-06T18:00:29.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/57/ecc9ae29fa5b2d90107cd1d9bf8ed19aacb74b2264d986ae9d44fe9bdf87/debugpy-1.8.16-py2.py3-none-any.whl", hash = "sha256:19c9521962475b87da6f673514f7fd610328757ec993bf7ec0d8c96f9a325f9e", size = 5287700, upload-time = "2025-08-06T18:00:42.333Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.2.1"
|
||||
@@ -988,7 +1027,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "e2b"
|
||||
version = "1.11.1"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
@@ -999,23 +1038,23 @@ dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b4/49/2110d31074da3ba233984c6c02fa5def9ab043669cdb921f380b71457329/e2b-1.11.1.tar.gz", hash = "sha256:7f7b6f238208d0a23353bb0da01f91a924321b57c61b176506862cbc1493ce8c", size = 61926, upload-time = "2025-08-06T10:31:42.747Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/61/4cacf82b1d00f888678fdcfff23a8c3a09aed25b2993eea7af472e20dda5/e2b-2.0.0.tar.gz", hash = "sha256:4d033d937b0a09b8428e73233321a913cbaef8e7299fc731579c656e9d53a144", size = 66401, upload-time = "2025-08-21T15:50:40.207Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/a4/be83c8b1cd923b558fb76a0fcbc4172b5348c956c706c70e19c038bc37f8/e2b-1.11.1-py3-none-any.whl", hash = "sha256:1ecb123873788472731c101939a494ab852cbcce0f913df6f7ecb194ae932130", size = 114762, upload-time = "2025-08-06T10:31:40.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/76/ddf676a327006b9c3bdec1a10b99aad4f10af8b2cbc3358ebb156951914e/e2b-2.0.0-py3-none-any.whl", hash = "sha256:a6621b905cb2a883a9c520736ae98343a6184fc90c29b4f2f079d720294a0df0", size = 123785, upload-time = "2025-08-21T15:50:38.579Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e2b-code-interpreter"
|
||||
version = "1.5.2"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
{ name = "e2b" },
|
||||
{ name = "httpx" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/85/b4a1c9427b45818d4c3773ed967ec1fcc2d7b677e096d8051303889adc2d/e2b_code_interpreter-1.5.2.tar.gz", hash = "sha256:3bd6ea70596290e85aaf0a2f19f28bf37a5e73d13086f5e6a0080bb591c5a547", size = 10006, upload-time = "2025-07-07T14:58:28.676Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/84/e94cb706d88ac3510748be9d0030fb0a896a25b43677903aff20213b3cc1/e2b_code_interpreter-2.0.0.tar.gz", hash = "sha256:19136916be8de60bfd0a678742501d1d0335442bb6e86405c7dd6f98059b73c4", size = 10029, upload-time = "2025-08-22T10:16:57.169Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/4a/7dc5c673c47418e1b38594ab3b022ee20ea1bf3ff8f8aa8273d6ddc99532/e2b_code_interpreter-1.5.2-py3-none-any.whl", hash = "sha256:5c3188d8f25226b28fef4b255447cc6a4c36afb748bdd5180b45be486d5169f3", size = 12873, upload-time = "2025-07-07T14:58:27.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/f1/135acbaffe4b2e63addecfc2a6c2ecf9ea3e5394aa2a9a829e3eb6f2098d/e2b_code_interpreter-2.0.0-py3-none-any.whl", hash = "sha256:273642d4dd78f09327fb1553fe4f7ddcf17892b78f98236e038d29985e42dca5", size = 12939, upload-time = "2025-08-22T10:16:55.698Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1038,14 +1077,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "faker"
|
||||
version = "37.5.3"
|
||||
version = "37.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tzdata" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/5d/7797a74e8e31fa227f0303239802c5f09b6722bdb6638359e7b6c8f30004/faker-37.5.3.tar.gz", hash = "sha256:8315d8ff4d6f4f588bd42ffe63abd599886c785073e26a44707e10eeba5713dc", size = 1907147, upload-time = "2025-07-30T15:52:19.528Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/cd/f7679c20f07d9e2013123b7f7e13809a3450a18d938d58e86081a486ea15/faker-37.6.0.tar.gz", hash = "sha256:0f8cc34f30095184adf87c3c24c45b38b33ad81c35ef6eb0a3118f301143012c", size = 1907960, upload-time = "2025-08-26T15:56:27.419Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/bf/d06dd96e7afa72069dbdd26ed0853b5e8bd7941e2c0819a9b21d6e6fc052/faker-37.5.3-py3-none-any.whl", hash = "sha256:386fe9d5e6132a915984bf887fcebcc72d6366a25dd5952905b31b141a17016d", size = 1949261, upload-time = "2025-07-30T15:52:17.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/7d/8b50e4ac772719777be33661f4bde320793400a706f5eb214e4de46f093c/faker-37.6.0-py3-none-any.whl", hash = "sha256:3c5209b23d7049d596a51db5d76403a0ccfea6fc294ffa2ecfef6a8843b1e6a7", size = 1949837, upload-time = "2025-08-26T15:56:25.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1488,65 +1527,68 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "granian"
|
||||
version = "2.5.0"
|
||||
version = "2.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/91/6b51c5749a58e5d86063b193c15914700464f0d64eda84178bf432dbbcf9/granian-2.5.0.tar.gz", hash = "sha256:bed0d047c9c0c6c6a5a85ee5b3c7e2683fc63e03ac032eaf3d7654fa96bde102", size = 110336, upload-time = "2025-07-30T18:55:15.161Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/18/11085d3d7c97ff93501ae6acc8217943d9e8f2ac45d9baf51ab146a2355b/granian-2.5.1.tar.gz", hash = "sha256:c268be38053bd29351bf8f86d87a5862708033ee5322ac47465b99ff45670783", size = 111858, upload-time = "2025-08-26T16:11:09.204Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f3/7d9cee103d91f4d1b934ebaa0cea944638a7b4940c5af72163e486cd4989/granian-2.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0eda4c389c222aa5455b7205640df0207201a86c46e5be98dd0040b6cc45146a", size = 3044837, upload-time = "2025-07-30T18:52:49.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/b8/fcb93a7bddcedc0af11a446094b33dc99af93a338abd8e95747aae3d1112/granian-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:58aea28ebb2cdf7545ee3cb1c8593c8b2f857a9fa6219589ecd3f5a4b365262f", size = 2602770, upload-time = "2025-07-30T18:52:51.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/d9/5f94af3cbdbe023774d24616648e428cfd307f18232a64d82caa2ad8113a/granian-2.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf9dc480d481ae834a085f1f46213ebf80512b1ace0559f6c0335edb24be0e92", size = 3347657, upload-time = "2025-07-30T18:52:53.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/12/ba9be1b7a9ad28b66735a648a996578b64873c580a3a0681575e60cfa0f8/granian-2.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:604c544273b36091b54fdf66d4e9a0f98dc0369b380ba5dd328478ba65cda320", size = 2949970, upload-time = "2025-07-30T18:52:55.359Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/54/f29100152e7dd6f5dfdff2626b040711735aff2ec9f61cba8e7d04614a5e/granian-2.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f6d70f6e7edd183afb62468a7fc175348145aec303297a41b1714a9b6d8150d", size = 3233777, upload-time = "2025-07-30T18:52:57.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/e0/988993586ba3d5e80cc87fc464601df55634ec440eb10889c8fdd3b613ac/granian-2.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:651cbad3a137b762885b7e57dea77b5d11262b0a2c16d4b61b4812bc8bc5ffa4", size = 3108386, upload-time = "2025-07-30T18:52:58.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/06/815fde5195f40a2a1be4e78ad0c3cdd05727dcca59a5a231d0df95fb6f68/granian-2.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:22bac6aace54e4183831a46a4033c076100150527676e666eeb84df9829e0e1c", size = 3114733, upload-time = "2025-07-30T18:53:00.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/7f/a8d2fce7f810aa3b7b16550cfbde4756eeb3efcd3646b0adb6c6b7d4bf95/granian-2.5.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:0429dd0c4c21c123b06b7f9057ecb4c1fc3d6228f442276e27545a5ad6fc780c", size = 3495857, upload-time = "2025-07-30T18:53:01.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/58/984b53efc3b245f5f9b8d76822061b3fb4c5c1024d526ffe59da55b5405c/granian-2.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c6141582757eb2bb8c8390637a3ac29dbba0c10db93192adb360c7060f149782", size = 3273079, upload-time = "2025-07-30T18:53:02.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/08/047b3004ebbad8934b4b963c1ab5a0cd449c94c68f95557d57a7d561695d/granian-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:29da6a1c6dfc728fc96649b514e26b38848e40e4e78676f51f2ed90c51da733e", size = 2331232, upload-time = "2025-07-30T18:53:04.174Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/31/590b932524f43289aa9f735d0b92ccdd97b2d9e388a5acad171fc01382e4/granian-2.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e7627c3b7e3f9c024c4edd80636e8326fbce0420889e0951da349d13742e503", size = 3026668, upload-time = "2025-07-30T18:53:05.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/94/6bcd3d0cec40994112dfd2b3102f4ff3bd2e62928f6524fb95f38fae6647/granian-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0a4c317f30c227baf16f541f0c93cb08ee45fbf8a2ef5317ba07b6bd6b7c877", size = 2584723, upload-time = "2025-07-30T18:53:06.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/5b/44089c2384ba2e3e5a3cdf08e8a000bff07cf7382fa2d9a0e4e1a9ba6451/granian-2.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35eb58b0f80fc9f55d5210d339ba8f5f8d9c126a2e29f051e8b62353e3f84b1d", size = 3326781, upload-time = "2025-07-30T18:53:08.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/7d/7ca304dde1ce475b83e4add007e36a87284e6838f158db87c11a2c93f379/granian-2.5.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def250f04c0374069278152bf3e08ecb1f67e0c99d3eb14d902df1e1558b93fa", size = 2937454, upload-time = "2025-07-30T18:53:10.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/6c/858a7cce6ce07adc2f0e7b1809af61d1af2affbc07edaecd127f16207b37/granian-2.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eda01a9027f2921f42b4f8bb16e46f8ab67a5345e52b3ffeedd2f921a09c87b6", size = 3229723, upload-time = "2025-07-30T18:53:11.464Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/7b/ded645443ec95921d407e3d277418987a4aed955830f67d6b151c8c095f9/granian-2.5.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c8a51bcb7e533ab75d2d9e13432e9b63c90eeb7fdde700875253efbfb2dcdcbc", size = 3109804, upload-time = "2025-07-30T18:53:13.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/64/837131cd49e17c219e2a04ed711459e0f93bd5c1db7fc243666e1b9d412a/granian-2.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a9c5670171a97c35aeea79fd7100058e461b0271a7e81fdecd586c770cdd2b41", size = 3099711, upload-time = "2025-07-30T18:53:14.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/4e/80578d06426a40a2718b3183c5e32ba570001153cbbb3fe523d3f9e89880/granian-2.5.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:760275d286142775fb21c85d96fc4996e00de9d3b054c424b2c8519679aa14b5", size = 3468897, upload-time = "2025-07-30T18:53:16.477Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/8c/7e5d0187a4e53830cc49231a55f01d3d251eec0cbb09209a0ffde8b33741/granian-2.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1573755288d70f37b55acb14f01cb3a7f7cdca7bf143f908c649ada254e9cb6", size = 3275035, upload-time = "2025-07-30T18:53:17.947Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/5c/3176f48ef4c723a0bd5143b59c598957749d05a4f88db5e03760858deae7/granian-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f60c46cf8684a6b5c1d9bf88cc9a248682a753874e14bd2cc6c81c2001449dab", size = 2321533, upload-time = "2025-07-30T18:53:19.589Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/d8/c3c8a452f1b590400bb2cdef1ca61da8e9913762884cf3e6ba801fd3fdad/granian-2.5.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:50d4dc74ab763c1bf396cf85d93a8202bf1bfb74150b03f9fd62b600cd0c777c", size = 3026123, upload-time = "2025-07-30T18:53:20.94Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/f0/e7038189d4e3b5f1e10bc23547b687bcdecdefbddef87013db64efea6800/granian-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:31705782cd616b9b70536c1b61b7f15815ebc4dcccdb72f58aa806ba7ac5dfa1", size = 2584469, upload-time = "2025-07-30T18:53:22.579Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/2d/718620f393b6030d4a9ac5d1bc66cc5d159fb7f7c60c5d4483fd43902f7d/granian-2.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bbc4ebc727202ad4b3073ca8148c2af49904710d6fce84872191b2dd5cd36916", size = 3326593, upload-time = "2025-07-30T18:53:24.288Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/57/c8b5f014673717e850db6d551057e74e330aadad5250d3f312cee432b1ec/granian-2.5.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af272218076663280fdc293b7da3adb716f23d54211cefad92fcf7e01b3eed19", size = 2937464, upload-time = "2025-07-30T18:53:25.72Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/91/8ae8aa9f0c3bbdf42fada15d623a5ab9fffd38acc243ec5619b6cbd60b9a/granian-2.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36493c4f2b672d027eb11b05ca6660f9fd4944452841d213cb0cb64da869539b", size = 3229316, upload-time = "2025-07-30T18:53:27.262Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/8c/6c294cf3d77dc9524530d30057f9b6e334cc12c5414feb604fb277d030a3/granian-2.5.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:afafac4908d5931e4b2c2a09612e063d7ccd05e531f16b7f11e3bccc4ca8972c", size = 3109818, upload-time = "2025-07-30T18:53:29.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/49/acc3f1e02e35009d9486e4e00d2c951798a8098935d2374f52c7d2728438/granian-2.5.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:fb157c3d66301ffad4113da4c51aed4d56006b9ebe9d0892c682a634b5fff773", size = 3099384, upload-time = "2025-07-30T18:53:30.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/87/7cdd96fbeabbceea3820736e65bd6d8c0021983605cee26ef1bf2e11e24b/granian-2.5.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:879fdeb71fe279175a25d709d95dd2db01eb67cd12d300e51e3dc704ca5e52fd", size = 3468575, upload-time = "2025-07-30T18:53:31.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/64/bd41efc6bfbca0ff871ce28a13b9e687055dd70913dfce92c4a21a264bf7/granian-2.5.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:74601bda3aedb249a3d5059d48108acfa61d6f71686162bda0bedc013a443efb", size = 3274703, upload-time = "2025-07-30T18:53:33.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/42/c1a8f51d7ce3408a6aeebf68338e0282f0123b65c2fef1d7c202a407062d/granian-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:76dc084d1c528683a88c2d1a00786c9bc013b695b1776ad8a3c71419c45e1df0", size = 2321042, upload-time = "2025-07-30T18:53:34.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/43/af71556ea889c28b8c1c74e9f50a64c040a92bae5e4412b8617638a8aa0e/granian-2.5.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f371dd9eedae26158901fee3eb934e8fa61491cc78d234470ce364b989c78a1f", size = 2955162, upload-time = "2025-07-30T18:53:36.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/35/14c2c050f3df95eb054f2a44b41a02c30c8a04dc8cca888330f55c43a436/granian-2.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f7bf7ed30bcda9bbc9962f187081c5dfa6aa07e06c3a59486bc573b5def35914", size = 2548356, upload-time = "2025-07-30T18:53:38.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/b3/8944acd78ff37a2effcdaf1d6163179e38c46c67c98bb1a68e93a75eb2c2/granian-2.5.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3152037d799ea97e5736de054a48bf75368fb79b7cfee7e6aa46de1076a43882", size = 3091535, upload-time = "2025-07-30T18:53:39.514Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/49/cf0c89eaa41ac81271e3ae33834f71db945cc09dba609f6dc0e75247fd35/granian-2.5.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:9a53151c2d31dbcf1acbe6af89ce0282387614b6401650d511ca4260ba0e03c1", size = 2977716, upload-time = "2025-07-30T18:53:41.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/58/e828bd5a02c412484b4056cef9aa505b014cc0bb1882f5dbdaf26782f147/granian-2.5.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:8f9918bee3c21eb1410f4323440d76eaa0c2d2e6ca4fa3e3a20d07cc54b788f6", size = 3094250, upload-time = "2025-07-30T18:53:42.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/ba/e70f0de5cd6e5bca15ea5e5bc6c5598d34f9d4e9e6707e79e6edb63f1fac/granian-2.5.0-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:c28a34951c1ed8eea97948882bdbc374ce111be5a59293693613d25043ba1313", size = 3458806, upload-time = "2025-07-30T18:53:44.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/c5/4d45042c86a924703f0c9617859742e929cdfe31b644bb16f9845c75342c/granian-2.5.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:944ea3bd400a7ccc8129835eda65bd6a37f8fb77828f4e6ded2f06827d6ec25f", size = 3254382, upload-time = "2025-07-30T18:53:45.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/8e/bbe7564e28385ebb35e94dd7127238b9ba5c29ee09791f3f69d77b3985d4/granian-2.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9f6d080e45735dd93e4c60a79e42ee9ed37124a9580a08292d83b0961c705e39", size = 2312124, upload-time = "2025-07-30T18:53:47.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/ee/97010d532c4ac48fb2c6dd03e296c8d6ad5829e09f399bfd0a33b6098953/granian-2.5.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a80f23e904f23ca9a90d613444a477a8df8bb2ef1df7bf279ffa6ab7cbbf042a", size = 3034092, upload-time = "2025-07-30T18:54:46.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/f9/263fd8d1d2f0904ead415a19a1b05980180ee647edcb6b0727e2a942e4ad/granian-2.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:53b10f7996c9a732cb3e4cf30890badbac5f9ec4baa2898851a68100767cc754", size = 2601634, upload-time = "2025-07-30T18:54:47.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/a0/2a3348f6a1291a50cc0b6535b6cdf7fbb73998a5ced6536c0026834a781d/granian-2.5.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1a05836852ff70745ec094940d9edc62bb3f1a1f7ffb8ee692d6727ebc8b95", size = 3216203, upload-time = "2025-07-30T18:54:49.404Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/5f/f18826ae61861c6e20a1887a359c71074f423e4cd7237faf186618d0f7ee/granian-2.5.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3ce4c44ebb949980cc02e0c8a823fb4352c94f2443004fe4c39eb0262fcb2e6e", size = 3106525, upload-time = "2025-07-30T18:54:50.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/51/6776580a11e0966af4919735072b7d6fd95feea2907753a77046f1f8fbe3/granian-2.5.0-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5a8eea72a37c582fe2653587f5b4bb5323bd8882fcbccff3054311b0735d3814", size = 3106320, upload-time = "2025-07-30T18:54:53.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/8f/9910ac8585fe0f8f0d55bb197a08cccea27e7cd778d059302e5d4c78dfdf/granian-2.5.0-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:168762227d94b74dddff066b2f3519f22426e09f8394ed1a2f48072f80be9275", size = 3533584, upload-time = "2025-07-30T18:54:54.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/49/24c811233d11756182ad13dd30e5323dd88faeb76879493dccb484f8d8fe/granian-2.5.0-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:534a0922c460a8bf9c85937edbc7aac00497d05b64bf9ccc5f5b93006882ecd7", size = 3272305, upload-time = "2025-07-30T18:54:56.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/07/a39bb33f26c422d5548d9b928fc06da41f50314d3f47ed443791be51d147/granian-2.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6bd767b7f456472eef6597570c588ce8ff2351809cd64ecbb5e0b4f41f74044d", size = 2329707, upload-time = "2025-07-30T18:54:58.531Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/20/d9dbfca0979dfdb6a964520c2a4c92591ce33abe00747bcaf94c337bb438/granian-2.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2fcb9fd1c859d7576ff5262f01ed53981c70c831b3f64490333a4012c51aa338", size = 2847793, upload-time = "2025-08-26T16:08:32.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/ea/227d1e42ad86505bf49301b0047697f47e9b4ed61d8ce921d6f8d888a3e5/granian-2.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6fa641231b0e9ee89c72dcd3610fba9ffa0aa89ddab62a3da460d5bce2f13c1d", size = 2550025, upload-time = "2025-08-26T16:08:34.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/b7/0bea2dae12f78b0fd4dd9f2219817ba2417cd4a3a9d9000c088554986a56/granian-2.5.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a087b155f595c127f3dc331bc067ece1d55da5a5984649bf708cdee4b65d71cb", size = 3037651, upload-time = "2025-08-26T16:08:35.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/9e/5000edbd59a0f802930ed217a42f14a66835dda075c55d3ba433088fb2ef/granian-2.5.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:873eb7e402ca59484ee8e41d495c6e8c7a659dd4bea4a72f711f6f5d591c6400", size = 2860856, upload-time = "2025-08-26T16:08:37.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/ee/bd4d7746523105dd6750607b7c21577f2562d870ab24bb1f97a828d98f29/granian-2.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b4319ce47b218bbf10e39affdf935f3caaf996f1c82fd9539bbe1086e9b636a", size = 3164156, upload-time = "2025-08-26T16:08:39.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/0a/027bc8299d0d8e46b0a2529eb23937b934bd9e5f143311d4e3eb63bf4b96/granian-2.5.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:56651c3689daf8f133a786ce43c8f24926a75bdf61ed1f205c4648940dbb6e22", size = 2932547, upload-time = "2025-08-26T16:08:41.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/9f/65e1e328824fbbd2836969b8f8aa938df745df0219cb45201d0ab5a816a7/granian-2.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ec827847fd41241f294e47eeb58b9db22eca0375f1f3bcefed55718188c31748", size = 2914942, upload-time = "2025-08-26T16:08:43.092Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/3f/4dda1c00f3420278268c3e65caa4c6de28e67443f71e5177c8a77fb7b4c5/granian-2.5.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:f40ea10e7348011ca85edeeb04a2afb2eae6baf775a493222d607fa7a3b175cd", size = 3150697, upload-time = "2025-08-26T16:08:44.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/dc/0cea90f9654231dcaf428137d93d62827cc22de45adc952d8bfd006af08b/granian-2.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:73a5657783cc4eaa1ea68599f4b471c00e573d31c8c66c9b8cba78baaa258e87", size = 3197458, upload-time = "2025-08-26T16:08:46.37Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/01/ad821503d38a12f1028462ba702956a59f3172b6527960112787e9d85b7a/granian-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:bfa7d98c32757a1e079917607f8b65de4b6c886411efedbb03040dc7860121b1", size = 2187133, upload-time = "2025-08-26T16:08:47.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/09/faab23dc6f49fa34dd63870080e74016fbfc2e0dd3f34340f219da0dcd5f/granian-2.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9c609c0f41f5f3eaccf2c2b6e190b40f75686cb9ebda8db09133b10457ae218a", size = 2832263, upload-time = "2025-08-26T16:08:49.113Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/6c/d8564ab4eb4b84408739c930888a6f7b6db80fed8a5a1fde636d6887bdfc/granian-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4462fa0a2ce1b419fdd1dc1039c29101dd84537bbbf1358e99ee15b35683c88e", size = 2538574, upload-time = "2025-08-26T16:08:50.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/ef/62c7bc3e79d5bde01d5f452acf2974848514f92ad71c28dbd3688536b6f3/granian-2.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4ebfeb337f2f27cb7a5de6c5ae6ff309bb699cf4ac1f1714685650fb2daffeb", size = 3025666, upload-time = "2025-08-26T16:08:53.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/cd/bddcc1d15dd72530ac9fac930ebc0d94017020d35ef4497c871ff2adb7fa/granian-2.5.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a45be4bc3f387fcf90ab332e455ef59c7f35ae96bc64ed9e6cdc751c0c7530b7", size = 2858379, upload-time = "2025-08-26T16:08:55.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/73/0bdf768f57720182301e818e01a368048de9777ce775c16db91ebd1bd593/granian-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78172a29057f6d9794fd89c964eeb849dab7bc6b5f558a67daa0f10ed7fa742d", size = 3159084, upload-time = "2025-08-26T16:08:56.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/6a/20121270db16ea296372682e899a27a490445e1155eb49b7b3dee35a41b3/granian-2.5.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d086dc3509888b2855cfd7107cc30279ca004a8b40ab6e5bf12a6925649bf632", size = 2933814, upload-time = "2025-08-26T16:08:58.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/fe/2df61f54d24d53e2f326d6fad3ee409fcfd11424cf41d96c217a57c8219b/granian-2.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d45175bdf63ad9072a54c925f27114554ea3457d4a84d58cda84cb815d57178d", size = 2911839, upload-time = "2025-08-26T16:09:00.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/d1/da10aa21f539cafa7dfd2f829c2264549dc6778c471c4855cf91c5b69b01/granian-2.5.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:20dcdd87134ea43a5deea9926ccf94b841a5d212801d4901e5316c3f0fee7a65", size = 3137107, upload-time = "2025-08-26T16:09:01.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/51/f94c8a26871f5e5f74d03f6bb4c1c588f0f11385107435eceb7d5b3a9834/granian-2.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:eec44e5687d90b66b27477bc9b51e272cf884ff0260d31222a6a0651600c5cf5", size = 3210850, upload-time = "2025-08-26T16:09:03.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/c5/4010b30f02f0b3627aedc0e246bd7a6563f2313529e3e79fa1d592216161/granian-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:999d6dbe6ddf7e96484848da6b1ecd41e77f29e5f21a7155186c11d1f258f1f2", size = 2189562, upload-time = "2025-08-26T16:09:04.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/dd/9b22b8e52d17c5472c061c81a14bb3b4cbb80ce37df33465775ff28781d5/granian-2.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:673fd75d5c523238842abd2dfbbf13733f450e4734a16b01aedf2bdf8cf69881", size = 2831999, upload-time = "2025-08-26T16:09:06.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/e5/2d09f60e2140b738518356690877e72d8be64726c652ec849cdfcd1f794a/granian-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d3c2275b1346e6445cd92fef3a67f5de8871150f3c71d20209c0f0974ce690d", size = 2538199, upload-time = "2025-08-26T16:09:07.781Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/8a/29f07695cdb6f08e80bf7748f9bfb68d1e0f70c57e70049780d657caa1f4/granian-2.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04e728c12c0b3181bec64b09948e29045cf305128571ec2119c68b9904222b21", size = 3025728, upload-time = "2025-08-26T16:09:09.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/d3/0035137dc8f2637018fbb3e5cb67c83bc4ff44f18b9ccb7bd6cec288471d/granian-2.5.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24d7ad8616f5871a2bae40cfbc9476c65a77640c0eda0b4cb2fda882d773d451", size = 2858025, upload-time = "2025-08-26T16:09:10.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/75/19e8ce4c39ba3d84603e1d31a06b2f9af28e747ad340aa344468b4f87faa/granian-2.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:914e571434bbfa0226e16a14409a088031cac7015c191614e936c64d60941744", size = 3158731, upload-time = "2025-08-26T16:09:12.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/ea/a280c781a3749988a46c0507c9964aad9f774e9564870401fc96167a55c6/granian-2.5.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a78afa9b7e328557ca3ec6cc7970c94cc7c7a2a1cb5c48801a884df14169d657", size = 2933432, upload-time = "2025-08-26T16:09:13.695Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/dd/2ff23d8c338eaa47f081103c2bc33b1be403a9c5bc0fe18c5815cc86dd9d/granian-2.5.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:32974ba79997d50dca0ecaee8068b71630a0afbcb1b2f2aaa1a45d01a4fe81d3", size = 2911297, upload-time = "2025-08-26T16:09:15.134Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/19/9306cd660df0492a59a786b19f0689e4b5052d842541326ff3deb1444a9f/granian-2.5.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:4b1adddd027ec83a8000d7ea3dd3f7c7094e811f5a76a057d72e6f97d5f520ba", size = 3137004, upload-time = "2025-08-26T16:09:16.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/10/ffe61ed0ca7ce2cb5bf7e37f31ebe27d1bd2693b4f05389db8f5b15f2f03/granian-2.5.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8dbbcba5b3a0b76c4408106260b3f9a13d5946b611303c7f0916c60a5efb6ff5", size = 3210685, upload-time = "2025-08-26T16:09:18.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/60/382a0a22885d6be74678f88a570b258c379111fcbccd79de934cfd8496c3/granian-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:f56f32e7668b5d8b2f76c56a001b0053c775362d3117288cdbb1fb54afb4403c", size = 2189261, upload-time = "2025-08-26T16:09:20.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/d1/9af9f258c60fffc45901746879d008ed1303edea45cc150e6c9a28709c76/granian-2.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:913343c63ca84c76f864b73170fe9b13e197e081b70d0f8264d0e7ba305f24bd", size = 2769579, upload-time = "2025-08-26T16:09:21.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/cf/8f58e3e47bc9cc78e5beb181916434b4601426c66c062ab8a8217626aad9/granian-2.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:576f539cb5acb35b24ef1106e9be34b53f1b9c8bd87e60d90e371ddb3ed1f5af", size = 2487354, upload-time = "2025-08-26T16:09:23.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/83/b6fae91a9306aef0670ebf8b05a5a61624b8bcd9899a351fb46c03554b6d/granian-2.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bb9aeadd6c843dc08c59f41f8e5c3de5f463eef069544ae2e18bea08d2158cb", size = 3010060, upload-time = "2025-08-26T16:09:25.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/f9/5ade2b1f7d0f4a8b45a64aa533234df07d721e0a9d8495070324850be0c9/granian-2.5.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:19fd929320f1fa9ddda569166a97427dc7f0cd92819bba6ca78e10435f4d7c02", size = 2787283, upload-time = "2025-08-26T16:09:27.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/a4/2c87003d9fa902488b7da880da5eb7f6a270519e82d967f422ac89de3fcb/granian-2.5.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:ce8f94dfd3872fd6db5f18040b66e64c82506d19cb908a98f152ec6a58360840", size = 2905213, upload-time = "2025-08-26T16:09:28.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/87/5db2bb3b2607bdbf5880e42e16e7c66713915f88500493bfb5c53f665cc8/granian-2.5.1-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:f69b103556075f0156f796ee80bfcc7ad7743821b54dc33bc2e4ca20ed2b65ce", size = 3119807, upload-time = "2025-08-26T16:09:30.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/20/689ca4eea01f907fdc740b0a344e650fd33cf36ee62326deedcd13d58f8a/granian-2.5.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:3cab879ebff5efd65782200783c73e8ee963eaee59a4a0f33351c8cdb35656a9", size = 3198431, upload-time = "2025-08-26T16:09:32.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/d1/af6bee60a27ac77c5c79f51d60d15c94e98af7408d661ef3343cd6ff14f6/granian-2.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:ea6303210fde16c1ad2b2575d396a218ca7195a5fb64640ccbcd6f9fb435c3a1", size = 2180388, upload-time = "2025-08-26T16:09:34.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/47/6bb6880cd97cc992185e404c216c85748d287bfe16dbc0c2e418935b6a3c/granian-2.5.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:08d92bdc91d91f0b5377a932faea36a640659994aa144d264995418992a4e01e", size = 2847147, upload-time = "2025-08-26T16:10:40.115Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/19/d606fa7383d5d20c7d8fe861ae07c61947c1bc41ef38cb0b2ef50b56dbd1/granian-2.5.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c6169cd8d19f6d8ef4b7b67afe618b8a5ceafd9ac7430da7dadb282c1a35f67", size = 2547108, upload-time = "2025-08-26T16:10:41.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/00/7b8ff7963510fbcb0fd6971eb098ad3099d529550347eb4bf2a24b3c6ea8/granian-2.5.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a469d1fd32923414926bfd4cc59c3e53bcfddbcea38409b09cbb0caf8823c75", size = 3158505, upload-time = "2025-08-26T16:10:43.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/0a/a59e2ae4eacb60456d186627714268741b1c25eacaa225d6a74853811337/granian-2.5.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:709014d3d103a843fe7db83ed77ad4781cba414c191428be7f94c5ada7151990", size = 2925033, upload-time = "2025-08-26T16:10:45.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/6e/c7952d669913fd876bc1428e8acc662ed2fdda4e23a4e36ca449ec843401/granian-2.5.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2bffcf01b067b109bf6e15410d0a7ea6bad45d69cb51b0661435815b57e71e23", size = 2913947, upload-time = "2025-08-26T16:10:47.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/97/b25bb52f3b43bd1baa3190b8bb7c837316e5bbca4c9a86c46c02d59f896e/granian-2.5.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3c822ec0c88cdb5be7323f72e0a78ff29e36a8dec5c2c60e83797173562cf395", size = 3171770, upload-time = "2025-08-26T16:10:49.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/1e/e6d7c65d4745ce429daf396685345bde8fb82d52b148fb8c8434f5f98298/granian-2.5.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:67bcecb791de0d63fed6d0c2c76efcdc12d046e63d9db3edb3ae3bf9881a3105", size = 3192802, upload-time = "2025-08-26T16:10:51.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/d5/c3592c66f2409f8c9f6867314f74d0bd9f9ad8a8c41bde49b97923875484/granian-2.5.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:985a3b184a144767e3aaa836d4ff8f9a1ae20cedebc363529dce3e7a0c795f6e", size = 2195202, upload-time = "2025-08-26T16:10:53.306Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
reload = [
|
||||
{ name = "watchfiles" },
|
||||
]
|
||||
uvloop = [
|
||||
{ name = "uvloop", marker = "platform_python_implementation == 'CPython' and sys_platform != 'win32'" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
@@ -1585,14 +1627,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "griffe"
|
||||
version = "1.12.1"
|
||||
version = "1.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/ca/29f36e00c74844ae50d139cf5a8b1751887b2f4d5023af65d460268ad7aa/griffe-1.12.1.tar.gz", hash = "sha256:29f5a6114c0aeda7d9c86a570f736883f8a2c5b38b57323d56b3d1c000565567", size = 411863, upload-time = "2025-08-14T21:08:15.38Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b5/23b91f22b7b3a7f8f62223f6664946271c0f5cb4179605a3e6bbae863920/griffe-1.13.0.tar.gz", hash = "sha256:246ea436a5e78f7fbf5f24ca8a727bb4d2a4b442a2959052eea3d0bfe9a076e0", size = 412759, upload-time = "2025-08-26T13:27:11.422Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/f2/4fab6c3e5bcaf38a44cc8a974d2752eaad4c129e45d6533d926a30edd133/griffe-1.12.1-py3-none-any.whl", hash = "sha256:2d7c12334de00089c31905424a00abcfd931b45b8b516967f224133903d302cc", size = 138940, upload-time = "2025-08-14T21:08:13.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/8c/b7cfdd8dfe48f6b09f7353323732e1a290c388bd14f216947928dc85f904/griffe-1.13.0-py3-none-any.whl", hash = "sha256:470fde5b735625ac0a36296cd194617f039e9e83e301fcbd493e2b58382d0559", size = 139365, upload-time = "2025-08-26T13:27:09.882Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1869,6 +1911,43 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/66/7f8c48009c72d73bc6bbe6eb87ac838d6a526146f7dab14af671121eb379/invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820", size = 160274, upload-time = "2023-07-12T18:05:16.294Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdb"
|
||||
version = "0.13.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "decorator" },
|
||||
{ name = "ipython" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042, upload-time = "2023-03-09T15:40:57.487Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130, upload-time = "2023-03-09T15:40:55.021Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipykernel"
|
||||
version = "6.30.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "appnope", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "comm" },
|
||||
{ name = "debugpy" },
|
||||
{ name = "ipython" },
|
||||
{ name = "jupyter-client" },
|
||||
{ name = "jupyter-core" },
|
||||
{ name = "matplotlib-inline" },
|
||||
{ name = "nest-asyncio" },
|
||||
{ name = "packaging" },
|
||||
{ name = "psutil" },
|
||||
{ name = "pyzmq" },
|
||||
{ name = "tornado" },
|
||||
{ name = "traitlets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/76/11082e338e0daadc89c8ff866185de11daf67d181901038f9e139d109761/ipykernel-6.30.1.tar.gz", hash = "sha256:6abb270161896402e76b91394fcdce5d1be5d45f456671e5080572f8505be39b", size = 166260, upload-time = "2025-08-04T15:47:35.018Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/c7/b445faca8deb954fe536abebff4ece5b097b923de482b26e78448c89d1dd/ipykernel-6.30.1-py3-none-any.whl", hash = "sha256:aa6b9fb93dca949069d8b85b6c79b2518e32ac583ae9c7d37c51d119e18b3fb4", size = 117484, upload-time = "2025-08-04T15:47:32.622Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "9.4.0"
|
||||
@@ -2067,6 +2146,36 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jupyter-client"
|
||||
version = "8.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jupyter-core" },
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "pyzmq" },
|
||||
{ name = "tornado" },
|
||||
{ name = "traitlets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jupyter-core"
|
||||
version = "5.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "platformdirs" },
|
||||
{ name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" },
|
||||
{ name = "traitlets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923, upload-time = "2025-05-27T07:38:16.655Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880, upload-time = "2025-05-27T07:38:15.137Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kiwisolver"
|
||||
version = "1.4.9"
|
||||
@@ -2151,7 +2260,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -2167,14 +2276,14 @@ dependencies = [
|
||||
{ name = "sqlalchemy" },
|
||||
{ name = "tenacity" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/76/200494f6de488217a196c4369e665d26b94c8c3642d46e2fd62f9daf0a3a/langchain_community-0.3.27.tar.gz", hash = "sha256:e1037c3b9da0c6d10bf06e838b034eb741e016515c79ef8f3f16e53ead33d882", size = 33237737, upload-time = "2025-07-02T18:47:02.329Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/08/76/35b698e00fd206eba44ca46d57860484e4b9f0e050fabac8d027e755935c/langchain_community-0.3.28.tar.gz", hash = "sha256:c97e03d91cade6c9fb73d756119744e1d4c4ea4b6b0a09f6faadfbb7360d335e", size = 33238079, upload-time = "2025-08-26T17:04:24.616Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/bc/f8c7dae8321d37ed39ac9d7896617c4203248240a4835b136e3724b3bb62/langchain_community-0.3.27-py3-none-any.whl", hash = "sha256:581f97b795f9633da738ea95da9cb78f8879b538090c9b7a68c0aed49c828f0d", size = 2530442, upload-time = "2025-07-02T18:47:00.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/6e/735e7e5376908b8dad204fa76d96a3e785d06c7b701660ad4f5efb6022cb/langchain_community-0.3.28-py3-none-any.whl", hash = "sha256:52e437b8f4e899ff59fb90c54b5320bf99153da34f214488ebacdbc969a50faf", size = 2530800, upload-time = "2025-08-26T17:04:21.873Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.74"
|
||||
version = "0.3.75"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@@ -2185,9 +2294,9 @@ dependencies = [
|
||||
{ name = "tenacity" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/c6/5d755a0f1f4857abbe5ea6f5907ed0e2b5df52bf4dde0a0fd768290e3084/langchain_core-0.3.74.tar.gz", hash = "sha256:ff604441aeade942fbcc0a3860a592daba7671345230c2078ba2eb5f82b6ba76", size = 569553, upload-time = "2025-08-07T20:47:05.094Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/63/270b71a23e849984505ddc7c5c9fd3f4bd9cb14b1a484ee44c4e51c33cc2/langchain_core-0.3.75.tar.gz", hash = "sha256:ab0eb95a06ed6043f76162e6086b45037690cb70b7f090bd83b5ebb8a05b70ed", size = 570876, upload-time = "2025-08-26T15:24:12.246Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/26/545283681ac0379d31c7ad0bac5f195e1982092d76c65ca048db9e3cec0e/langchain_core-0.3.74-py3-none-any.whl", hash = "sha256:088338b5bc2f6a66892f9afc777992c24ee3188f41cbc603d09181e34a228ce7", size = 443453, upload-time = "2025-08-07T20:47:03.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/42/0d0221cce6f168f644d7d96cb6c87c4e42fc55d2941da7a36e970e3ab8ab/langchain_core-0.3.75-py3-none-any.whl", hash = "sha256:03ca1fadf955ee3c7d5806a841f4b3a37b816acea5e61a7e6ba1298c05eea7f5", size = 443986, upload-time = "2025-08-26T15:24:10.883Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2204,7 +2313,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.4.16"
|
||||
version = "0.4.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
@@ -2215,9 +2324,9 @@ dependencies = [
|
||||
{ name = "requests-toolbelt" },
|
||||
{ name = "zstandard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a9/fb/a0fab0ce0bb46aaae9703c1fb814b4ac7cbc2d75adc51e8689f1b34ac08d/langsmith-0.4.16.tar.gz", hash = "sha256:a94f374c7fa0f406757f95f311e84873258563961e1af0ba8996411822cd7241", size = 930411, upload-time = "2025-08-22T15:45:16.56Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/65/4e79ad22cc12b31a87bdcf96b1ca5ddabe42a8494eda20e124d044d5562e/langsmith-0.4.18.tar.gz", hash = "sha256:c1340371119f66b7c506810c5998db3669cd04f018a276288d80b91169a68ccc", size = 931753, upload-time = "2025-08-26T17:00:05.901Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/ed/7a48189bdad850cfd47df671204c31779dd190de6bc681f169d4535f852e/langsmith-0.4.16-py3-none-any.whl", hash = "sha256:9ba95ed09b057dfe227e882f5446e1824bfc9f2c89de542ee6f0f8d90ab953a7", size = 375761, upload-time = "2025-08-22T15:45:14.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/73/91a506e17bb1bc6d20c2c04cf7b459dc58951bfbfe7f97f2c952646b4500/langsmith-0.4.18-py3-none-any.whl", hash = "sha256:ad63154f503678356aadf5b999f40393b4bbd332aee2d04cde3e431c61f2e1c2", size = 376444, upload-time = "2025-08-26T17:00:03.564Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2286,41 +2395,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
all = [
|
||||
{ name = "autoflake" },
|
||||
{ name = "black" },
|
||||
{ name = "docker" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "google-cloud-profiler" },
|
||||
{ name = "granian", extra = ["reload"] },
|
||||
{ name = "isort" },
|
||||
{ name = "langchain" },
|
||||
{ name = "langchain-community" },
|
||||
{ name = "locust" },
|
||||
{ name = "pexpect" },
|
||||
{ name = "pg8000" },
|
||||
{ name = "pgvector" },
|
||||
{ name = "pinecone", extra = ["asyncio"] },
|
||||
{ name = "pre-commit" },
|
||||
{ name = "psycopg2" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-order" },
|
||||
{ name = "redis" },
|
||||
{ name = "turbopuffer" },
|
||||
{ name = "uvicorn" },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32'" },
|
||||
{ name = "wikipedia" },
|
||||
]
|
||||
bedrock = [
|
||||
{ name = "aioboto3" },
|
||||
{ name = "boto3" },
|
||||
]
|
||||
cloud-tool-sandbox = [
|
||||
{ name = "e2b-code-interpreter" },
|
||||
{ name = "modal" },
|
||||
]
|
||||
desktop = [
|
||||
{ name = "docker" },
|
||||
@@ -2329,38 +2409,45 @@ desktop = [
|
||||
{ name = "langchain-community" },
|
||||
{ name = "locust" },
|
||||
{ name = "pgvector" },
|
||||
{ name = "pyright" },
|
||||
{ name = "sqlite-vec" },
|
||||
{ name = "uvicorn" },
|
||||
{ name = "websockets" },
|
||||
{ name = "wikipedia" },
|
||||
]
|
||||
dev = [
|
||||
{ name = "autoflake" },
|
||||
{ name = "black" },
|
||||
{ name = "black", extra = ["jupyter"] },
|
||||
{ name = "ipdb" },
|
||||
{ name = "ipykernel" },
|
||||
{ name = "isort" },
|
||||
{ name = "locust" },
|
||||
{ name = "pexpect" },
|
||||
{ name = "pre-commit" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-json-report" },
|
||||
{ name = "pytest-mock" },
|
||||
{ name = "pytest-order" },
|
||||
]
|
||||
experimental = [
|
||||
{ name = "google-cloud-profiler" },
|
||||
{ name = "granian", extra = ["reload"] },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32'" },
|
||||
{ name = "granian", extra = ["reload", "uvloop"] },
|
||||
{ name = "uvloop" },
|
||||
]
|
||||
external-tools = [
|
||||
{ name = "docker" },
|
||||
{ name = "firecrawl-py" },
|
||||
{ name = "langchain" },
|
||||
{ name = "langchain-community" },
|
||||
{ name = "turbopuffer" },
|
||||
{ name = "wikipedia" },
|
||||
]
|
||||
google = [
|
||||
{ name = "google-genai" },
|
||||
]
|
||||
modal = [
|
||||
{ name = "modal" },
|
||||
]
|
||||
pinecone = [
|
||||
{ name = "pinecone", extra = ["asyncio"] },
|
||||
]
|
||||
@@ -2383,10 +2470,6 @@ sqlite = [
|
||||
{ name = "aiosqlite" },
|
||||
{ name = "sqlite-vec" },
|
||||
]
|
||||
tests = [
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "wikipedia" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
@@ -2397,11 +2480,9 @@ requires-dist = [
|
||||
{ name = "anthropic", specifier = ">=0.49.0" },
|
||||
{ name = "apscheduler", specifier = ">=3.11.0" },
|
||||
{ name = "asyncpg", marker = "extra == 'postgres'", specifier = ">=0.30.0" },
|
||||
{ name = "autoflake", marker = "extra == 'all'", specifier = ">=2.3.0" },
|
||||
{ name = "autoflake", marker = "extra == 'dev'", specifier = ">=2.3.0" },
|
||||
{ name = "black", marker = "extra == 'all'", specifier = ">=24.2.0" },
|
||||
{ name = "black", marker = "extra == 'dev'", specifier = ">=24.2.0" },
|
||||
{ name = "black", extras = ["jupyter"], specifier = ">=24.2.0" },
|
||||
{ name = "black", extras = ["jupyter"], marker = "extra == 'dev'", specifier = ">=24.4.2" },
|
||||
{ name = "boto3", marker = "extra == 'bedrock'", specifier = ">=1.36.24" },
|
||||
{ name = "brotli", specifier = ">=1.1.0" },
|
||||
{ name = "certifi", specifier = ">=2025.6.15" },
|
||||
@@ -2409,48 +2490,41 @@ requires-dist = [
|
||||
{ name = "composio-core", specifier = ">=0.7.7" },
|
||||
{ name = "datamodel-code-generator", extras = ["http"], specifier = ">=0.25.0" },
|
||||
{ name = "demjson3", specifier = ">=3.0.6" },
|
||||
{ name = "docker", marker = "extra == 'all'", specifier = ">=7.1.0" },
|
||||
{ name = "docker", marker = "extra == 'desktop'", specifier = ">=7.1.0" },
|
||||
{ name = "docker", marker = "extra == 'external-tools'", specifier = ">=7.1.0" },
|
||||
{ name = "docstring-parser", specifier = ">=0.16,<0.17" },
|
||||
{ name = "e2b-code-interpreter", marker = "extra == 'cloud-tool-sandbox'", specifier = "==1.5.2" },
|
||||
{ name = "e2b-code-interpreter", marker = "extra == 'cloud-tool-sandbox'", specifier = ">=1.0.3" },
|
||||
{ name = "faker", specifier = ">=36.1.0" },
|
||||
{ name = "fastapi", marker = "extra == 'all'", specifier = ">=0.115.6" },
|
||||
{ name = "fastapi", marker = "extra == 'desktop'", specifier = ">=0.115.6" },
|
||||
{ name = "fastapi", marker = "extra == 'server'", specifier = ">=0.115.6" },
|
||||
{ name = "firecrawl-py", specifier = "==2.16.5" },
|
||||
{ name = "firecrawl-py", marker = "extra == 'external-tools'", specifier = "==2.16.5" },
|
||||
{ name = "google-cloud-profiler", marker = "extra == 'all'", specifier = ">=4.1.0" },
|
||||
{ name = "firecrawl-py", specifier = ">=2.8.0,<3.0.0" },
|
||||
{ name = "firecrawl-py", marker = "extra == 'external-tools'", specifier = ">=2.8.0,<3.0.0" },
|
||||
{ name = "google-cloud-profiler", marker = "extra == 'experimental'", specifier = ">=4.1.0" },
|
||||
{ name = "google-genai", marker = "extra == 'google'", specifier = ">=1.15.0" },
|
||||
{ name = "granian", extras = ["reload"], marker = "extra == 'all'", specifier = ">=2.3.2" },
|
||||
{ name = "granian", extras = ["reload"], marker = "extra == 'experimental'", specifier = ">=2.3.2" },
|
||||
{ name = "granian", extras = ["uvloop", "reload"], marker = "extra == 'experimental'", specifier = ">=2.3.2" },
|
||||
{ name = "grpcio", specifier = ">=1.68.1" },
|
||||
{ name = "grpcio-tools", specifier = ">=1.68.1" },
|
||||
{ name = "html2text", specifier = ">=2020.1.16" },
|
||||
{ name = "httpx", specifier = ">=0.28.0" },
|
||||
{ name = "httpx-sse", specifier = ">=0.4.0" },
|
||||
{ name = "isort", marker = "extra == 'all'", specifier = ">=5.13.2" },
|
||||
{ name = "ipdb", marker = "extra == 'dev'", specifier = ">=0.13.13" },
|
||||
{ name = "ipykernel", marker = "extra == 'dev'", specifier = ">=6.29.5" },
|
||||
{ name = "isort", marker = "extra == 'dev'", specifier = ">=5.13.2" },
|
||||
{ name = "jinja2", specifier = ">=3.1.5" },
|
||||
{ name = "langchain", marker = "extra == 'all'", specifier = ">=0.3.7" },
|
||||
{ name = "langchain", marker = "extra == 'desktop'", specifier = ">=0.3.7" },
|
||||
{ name = "langchain", marker = "extra == 'external-tools'", specifier = ">=0.3.7" },
|
||||
{ name = "langchain-community", marker = "extra == 'all'", specifier = ">=0.3.7" },
|
||||
{ name = "langchain-community", marker = "extra == 'desktop'", specifier = ">=0.3.7" },
|
||||
{ name = "langchain-community", marker = "extra == 'external-tools'", specifier = ">=0.3.7" },
|
||||
{ name = "letta-client", specifier = ">=0.1.276" },
|
||||
{ name = "letta-client", specifier = ">=0.1.277" },
|
||||
{ name = "llama-index", specifier = ">=0.12.2" },
|
||||
{ name = "llama-index-embeddings-openai", specifier = ">=0.3.1" },
|
||||
{ name = "locust", marker = "extra == 'all'", specifier = ">=2.31.5" },
|
||||
{ name = "locust", marker = "extra == 'desktop'", specifier = ">=2.31.5" },
|
||||
{ name = "locust", marker = "extra == 'dev'", specifier = ">=2.31.5" },
|
||||
{ name = "markitdown", extras = ["docx", "pdf", "pptx"], specifier = ">=0.1.2" },
|
||||
{ name = "marshmallow-sqlalchemy", specifier = ">=1.4.1" },
|
||||
{ name = "matplotlib", specifier = ">=3.10.1" },
|
||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.9.4" },
|
||||
{ name = "mistralai", specifier = ">=1.8.1" },
|
||||
{ name = "modal", marker = "extra == 'cloud-tool-sandbox'", specifier = ">=1.1.0" },
|
||||
{ name = "modal", marker = "extra == 'modal'", specifier = ">=1.1.0" },
|
||||
{ name = "nltk", specifier = ">=3.8.1" },
|
||||
{ name = "numpy", specifier = ">=2.1.0" },
|
||||
{ name = "openai", specifier = ">=1.99.9" },
|
||||
@@ -2461,41 +2535,29 @@ requires-dist = [
|
||||
{ name = "opentelemetry-sdk", specifier = "==1.30.0" },
|
||||
{ name = "orjson", specifier = ">=3.11.1" },
|
||||
{ name = "pathvalidate", specifier = ">=3.2.1" },
|
||||
{ name = "pexpect", marker = "extra == 'all'", specifier = ">=4.9.0" },
|
||||
{ name = "pexpect", marker = "extra == 'dev'", specifier = ">=4.9.0" },
|
||||
{ name = "pg8000", marker = "extra == 'all'", specifier = ">=1.30.3" },
|
||||
{ name = "pg8000", marker = "extra == 'postgres'", specifier = ">=1.30.3" },
|
||||
{ name = "pgvector", marker = "extra == 'all'", specifier = ">=0.2.3" },
|
||||
{ name = "pgvector", marker = "extra == 'desktop'", specifier = ">=0.2.3" },
|
||||
{ name = "pgvector", marker = "extra == 'postgres'", specifier = ">=0.2.3" },
|
||||
{ name = "pinecone", extras = ["asyncio"], marker = "extra == 'all'", specifier = ">=7.3.0" },
|
||||
{ name = "pinecone", extras = ["asyncio"], marker = "extra == 'pinecone'", specifier = ">=7.3.0" },
|
||||
{ name = "pre-commit", marker = "extra == 'all'", specifier = ">=3.5.0" },
|
||||
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.5.0" },
|
||||
{ name = "prettytable", specifier = ">=3.9.0" },
|
||||
{ name = "psycopg2", marker = "extra == 'all'", specifier = ">=2.9.10" },
|
||||
{ name = "psycopg2", marker = "extra == 'postgres'", specifier = ">=2.9.10" },
|
||||
{ name = "psycopg2-binary", marker = "extra == 'all'", specifier = ">=2.9.10" },
|
||||
{ name = "psycopg2-binary", marker = "extra == 'postgres'", specifier = ">=2.9.10" },
|
||||
{ name = "pydantic", specifier = ">=2.10.6" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.2.1" },
|
||||
{ name = "pyhumps", specifier = ">=3.8.0" },
|
||||
{ name = "pyright", marker = "extra == 'all'", specifier = ">=1.1.347" },
|
||||
{ name = "pyright", marker = "extra == 'desktop'", specifier = ">=1.1.347" },
|
||||
{ name = "pyright", marker = "extra == 'dev'", specifier = ">=1.1.347" },
|
||||
{ name = "pytest", marker = "extra == 'all'" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'all'", specifier = ">=0.24.0" },
|
||||
{ name = "pytest", marker = "extra == 'dev'" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'tests'", specifier = ">=0.24.0" },
|
||||
{ name = "pytest-order", marker = "extra == 'all'", specifier = ">=1.2.0" },
|
||||
{ name = "pytest-json-report", marker = "extra == 'dev'", specifier = ">=1.5.0" },
|
||||
{ name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.14.0" },
|
||||
{ name = "pytest-order", marker = "extra == 'dev'", specifier = ">=1.2.0" },
|
||||
{ name = "python-box", specifier = ">=7.1.1" },
|
||||
{ name = "python-multipart", specifier = ">=0.0.19" },
|
||||
{ name = "pytz", specifier = ">=2023.3.post1" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.1" },
|
||||
{ name = "questionary", specifier = ">=2.0.1" },
|
||||
{ name = "redis", marker = "extra == 'all'", specifier = ">=6.2.0" },
|
||||
{ name = "redis", marker = "extra == 'redis'", specifier = ">=6.2.0" },
|
||||
{ name = "rich", specifier = ">=13.9.4" },
|
||||
{ name = "sentry-sdk", extras = ["fastapi"], specifier = "==2.19.1" },
|
||||
@@ -2509,24 +2571,21 @@ requires-dist = [
|
||||
{ name = "structlog", specifier = ">=25.4.0" },
|
||||
{ name = "tavily-python", specifier = ">=0.7.2" },
|
||||
{ name = "tqdm", specifier = ">=4.66.1" },
|
||||
{ name = "turbopuffer", marker = "extra == 'all'", specifier = ">=0.5.17" },
|
||||
{ name = "turbopuffer", marker = "extra == 'external-tools'", specifier = ">=0.5.17" },
|
||||
{ name = "typer", specifier = ">=0.15.2" },
|
||||
{ name = "uvicorn", marker = "extra == 'all'", specifier = ">=0.24.0.post1" },
|
||||
{ name = "uvicorn", marker = "extra == 'desktop'", specifier = ">=0.24.0.post1" },
|
||||
{ name = "uvicorn", marker = "extra == 'server'", specifier = ">=0.24.0.post1" },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32' and extra == 'all'", specifier = ">=0.21.0" },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32' and extra == 'experimental'", specifier = ">=0.21.0" },
|
||||
{ name = "websockets", marker = "extra == 'server'", specifier = ">=12.0" },
|
||||
{ name = "wikipedia", marker = "extra == 'all'", specifier = ">=1.4.0" },
|
||||
{ name = "uvloop", marker = "extra == 'experimental'", specifier = ">=0.21.0" },
|
||||
{ name = "websockets", marker = "extra == 'desktop'" },
|
||||
{ name = "websockets", marker = "extra == 'server'" },
|
||||
{ name = "wikipedia", marker = "extra == 'desktop'", specifier = ">=1.4.0" },
|
||||
{ name = "wikipedia", marker = "extra == 'external-tools'", specifier = ">=1.4.0" },
|
||||
{ name = "wikipedia", marker = "extra == 'tests'", specifier = ">=1.4.0" },
|
||||
]
|
||||
provides-extras = ["postgres", "redis", "pinecone", "dev", "experimental", "server", "cloud-tool-sandbox", "external-tools", "tests", "sqlite", "bedrock", "google", "desktop", "all"]
|
||||
provides-extras = ["postgres", "redis", "pinecone", "sqlite", "experimental", "server", "bedrock", "google", "dev", "cloud-tool-sandbox", "modal", "external-tools", "desktop"]
|
||||
|
||||
[[package]]
|
||||
name = "letta-client"
|
||||
version = "0.1.277"
|
||||
version = "0.1.282"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
@@ -2535,9 +2594,9 @@ dependencies = [
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/07/ad/77af6bc5cebd280acc4c1e877ced058f3480fcd7bcb3986db613039a6485/letta_client-0.1.277.tar.gz", hash = "sha256:112837705c43c7c2c9a6baa99c38a0ad390f8bcb7ad0de5e3b9556990b898427", size = 183930, upload-time = "2025-08-25T20:50:20.217Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ef/11/5b22e4590ef9f9d52af7f282d7d373eefbf80cb35ece72a59acb182a5e65/letta_client-0.1.282.tar.gz", hash = "sha256:dc62b604171172fbab4b7259d769bf7a9b35e35242a37f9d0582376513c17168", size = 184433, upload-time = "2025-08-26T21:59:41.73Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/95/9bcfaac63d2467f07acfb85ad3497d4e1471fabb84ff95f3c493289cecd2/letta_client-0.1.277-py3-none-any.whl", hash = "sha256:2428ee84ff3cf582c935623dd7678845bfe46e79c75cb9a1a1b6efe284c5bc16", size = 464168, upload-time = "2025-08-25T20:50:18.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/08/3c16f38f25bb8704724f9336c01628eadaa0e43eb45f8dbdd2165d0aeb4b/letta_client-0.1.282-py3-none-any.whl", hash = "sha256:71a29e4061d9e66817514fab3e970940a9254067776f8c161a9bd325fe9ad5e7", size = 464951, upload-time = "2025-08-26T21:59:39.883Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3128,7 +3187,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mistralai"
|
||||
version = "1.9.8"
|
||||
version = "1.9.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "eval-type-backport" },
|
||||
@@ -3139,9 +3198,9 @@ dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/6f/12296d29c480a4101e2bc092347895ce1a8047b6bbc52f97f124177df0b4/mistralai-1.9.8.tar.gz", hash = "sha256:74eac8b3aee410dffbd8ef0878adb7f593940fa9592d9eb4428da9b067209b22", size = 204432, upload-time = "2025-08-25T16:30:31.354Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/37/02/fb484098d29f10e1f55f0c88c15262990d253304b94a5ecedd89e6a68d06/mistralai-1.9.9.tar.gz", hash = "sha256:025ae6f45dba8b7585642bc6fa214316138546a0cac692c6ec8e1187424da54a", size = 204678, upload-time = "2025-08-26T17:41:08.378Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/b3/3c1d449eea89153a77e7093e90e0282d1a718865ae6787c379256b1db288/mistralai-1.9.8-py3-none-any.whl", hash = "sha256:f4874d62932245c438c4fce04aaf740a9d6da651dc598bf660978a21fb73f017", size = 439113, upload-time = "2025-08-25T16:30:29.855Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/2c/7c62e67c221a10bfd94b219fe115211109a83e72a996fa343d72b4fa9f84/mistralai-1.9.9-py3-none-any.whl", hash = "sha256:6742fdbf4a277b605287538761e2665b6fb025328676a024868734ffe78ef72f", size = 439470, upload-time = "2025-08-26T17:41:07.05Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3446,7 +3505,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.101.0"
|
||||
version = "1.102.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -3458,9 +3517,9 @@ dependencies = [
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/00/7c/eaf06b62281f5ca4f774c4cff066e6ddfd6a027e0ac791be16acec3a95e3/openai-1.101.0.tar.gz", hash = "sha256:29f56df2236069686e64aca0e13c24a4ec310545afb25ef7da2ab1a18523f22d", size = 518415, upload-time = "2025-08-21T21:11:01.645Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/07/55/da5598ed5c6bdd9939633854049cddc5cbac0da938dfcfcb3c6b119c16c0/openai-1.102.0.tar.gz", hash = "sha256:2e0153bcd64a6523071e90211cbfca1f2bbc5ceedd0993ba932a5869f93b7fc9", size = 519027, upload-time = "2025-08-26T20:50:29.397Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/a6/0e39baa335bbd1c66c7e0a41dbbec10c5a15ab95c1344e7f7beb28eee65a/openai-1.101.0-py3-none-any.whl", hash = "sha256:6539a446cce154f8d9fb42757acdfd3ed9357ab0d34fcac11096c461da87133b", size = 810772, upload-time = "2025-08-21T21:10:59.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/0d/c9e7016d82c53c5b5e23e2bad36daebb8921ed44f69c0a985c6529a35106/openai-1.102.0-py3-none-any.whl", hash = "sha256:d751a7e95e222b5325306362ad02a7aa96e1fab3ed05b5888ce1c7ca63451345", size = 812015, upload-time = "2025-08-26T20:50:27.219Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3633,55 +3692,55 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "orjson"
|
||||
version = "3.11.2"
|
||||
version = "3.11.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/1d/5e0ae38788bdf0721326695e65fdf41405ed535f633eb0df0f06f57552fa/orjson-3.11.2.tar.gz", hash = "sha256:91bdcf5e69a8fd8e8bdb3de32b31ff01d2bd60c1e8d5fe7d5afabdcf19920309", size = 5470739, upload-time = "2025-08-12T15:12:28.626Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a", size = 5482394, upload-time = "2025-08-26T17:46:43.171Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/7d/e295df1ac9920cbb19fb4c1afa800e86f175cb657143aa422337270a4782/orjson-3.11.2-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:888b64ef7eaeeff63f773881929434a5834a6a140a63ad45183d59287f07fc6a", size = 226502, upload-time = "2025-08-12T15:10:42.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/21/ffb0f10ea04caf418fb4e7ad1fda4b9ab3179df9d7a33b69420f191aadd5/orjson-3.11.2-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:83387cc8b26c9fa0ae34d1ea8861a7ae6cff8fb3e346ab53e987d085315a728e", size = 115999, upload-time = "2025-08-12T15:10:43.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/d5/8da1e252ac3353d92e6f754ee0c85027c8a2cda90b6899da2be0df3ef83d/orjson-3.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e35f003692c216d7ee901b6b916b5734d6fc4180fcaa44c52081f974c08e17", size = 111563, upload-time = "2025-08-12T15:10:45.301Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/81/baabc32e52c570b0e4e1044b1bd2ccbec965e0de3ba2c13082255efa2006/orjson-3.11.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a0a4c29ae90b11d0c00bcc31533854d89f77bde2649ec602f512a7e16e00640", size = 116222, upload-time = "2025-08-12T15:10:46.92Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/b7/da2ad55ad80b49b560dce894c961477d0e76811ee6e614b301de9f2f8728/orjson-3.11.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:585d712b1880f68370108bc5534a257b561672d1592fae54938738fe7f6f1e33", size = 118594, upload-time = "2025-08-12T15:10:48.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/be/014f7eab51449f3c894aa9bbda2707b5340c85650cb7d0db4ec9ae280501/orjson-3.11.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d08e342a7143f8a7c11f1c4033efe81acbd3c98c68ba1b26b96080396019701f", size = 120700, upload-time = "2025-08-12T15:10:49.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/ae/c217903a30c51341868e2d8c318c59a8413baa35af54d7845071c8ccd6fe/orjson-3.11.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c0f84fc50398773a702732c87cd622737bf11c0721e6db3041ac7802a686fb", size = 123433, upload-time = "2025-08-12T15:10:51.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/c2/b3c346f78b1ff2da310dd300cb0f5d32167f872b4d3bb1ad122c889d97b0/orjson-3.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:140f84e3c8d4c142575898c91e3981000afebf0333df753a90b3435d349a5fe5", size = 121061, upload-time = "2025-08-12T15:10:52.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/c8/c97798f6010327ffc75ad21dd6bca11ea2067d1910777e798c2849f1c68f/orjson-3.11.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96304a2b7235e0f3f2d9363ddccdbfb027d27338722fe469fe656832a017602e", size = 119410, upload-time = "2025-08-12T15:10:53.692Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/fd/df720f7c0e35694617b7f95598b11a2cb0374661d8389703bea17217da53/orjson-3.11.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d7612bb227d5d9582f1f50a60bd55c64618fc22c4a32825d233a4f2771a428a", size = 392294, upload-time = "2025-08-12T15:10:55.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/52/0120d18f60ab0fe47531d520372b528a45c9a25dcab500f450374421881c/orjson-3.11.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a134587d18fe493befc2defffef2a8d27cfcada5696cb7234de54a21903ae89a", size = 134134, upload-time = "2025-08-12T15:10:56.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/10/1f967671966598366de42f07e92b0fc694ffc66eafa4b74131aeca84915f/orjson-3.11.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b84455e60c4bc12c1e4cbaa5cfc1acdc7775a9da9cec040e17232f4b05458bd", size = 123745, upload-time = "2025-08-12T15:10:57.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/eb/76081238671461cfd0f47e0c24f408ffa66184237d56ef18c33e86abb612/orjson-3.11.2-cp311-cp311-win32.whl", hash = "sha256:f0660efeac223f0731a70884e6914a5f04d613b5ae500744c43f7bf7b78f00f9", size = 124393, upload-time = "2025-08-12T15:10:59.267Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/76/cc598c1811ba9ba935171267b02e377fc9177489efce525d478a2999d9cc/orjson-3.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:955811c8405251d9e09cbe8606ad8fdef49a451bcf5520095a5ed38c669223d8", size = 119561, upload-time = "2025-08-12T15:11:00.559Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/17/c48011750f0489006f7617b0a3cebc8230f36d11a34e7e9aca2085f07792/orjson-3.11.2-cp311-cp311-win_arm64.whl", hash = "sha256:2e4d423a6f838552e3a6d9ec734b729f61f88b1124fd697eab82805ea1a2a97d", size = 114186, upload-time = "2025-08-12T15:11:01.931Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/02/46054ebe7996a8adee9640dcad7d39d76c2000dc0377efa38e55dc5cbf78/orjson-3.11.2-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:901d80d349d8452162b3aa1afb82cec5bee79a10550660bc21311cc61a4c5486", size = 226528, upload-time = "2025-08-12T15:11:03.317Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/c6/6b6f0b4d8aea1137436546b990f71be2cd8bd870aa2f5aa14dba0fcc95dc/orjson-3.11.2-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:cf3bd3967a360e87ee14ed82cb258b7f18c710dacf3822fb0042a14313a673a1", size = 115931, upload-time = "2025-08-12T15:11:04.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/05/4205cc97c30e82a293dd0d149b1a89b138ebe76afeca66fc129fa2aa4e6a/orjson-3.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26693dde66910078229a943e80eeb99fdce6cd2c26277dc80ead9f3ab97d2131", size = 111382, upload-time = "2025-08-12T15:11:06.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/c7/b8a951a93caa821f9272a7c917115d825ae2e4e8768f5ddf37968ec9de01/orjson-3.11.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad4c8acb50a28211c33fc7ef85ddf5cb18d4636a5205fd3fa2dce0411a0e30c", size = 116271, upload-time = "2025-08-12T15:11:07.845Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/03/1006c7f8782d5327439e26d9b0ec66500ea7b679d4bbb6b891d2834ab3ee/orjson-3.11.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:994181e7f1725bb5f2d481d7d228738e0743b16bf319ca85c29369c65913df14", size = 119086, upload-time = "2025-08-12T15:11:09.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/61/57d22bc31f36a93878a6f772aea76b2184102c6993dea897656a66d18c74/orjson-3.11.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb79a0476393c07656b69c8e763c3cc925fa8e1d9e9b7d1f626901bb5025448", size = 120724, upload-time = "2025-08-12T15:11:10.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/a9/4550e96b4c490c83aea697d5347b8f7eb188152cd7b5a38001055ca5b379/orjson-3.11.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:191ed27a1dddb305083d8716af413d7219f40ec1d4c9b0e977453b4db0d6fb6c", size = 123577, upload-time = "2025-08-12T15:11:12.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/86/09b8cb3ebd513d708ef0c92d36ac3eebda814c65c72137b0a82d6d688fc4/orjson-3.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0afb89f16f07220183fd00f5f297328ed0a68d8722ad1b0c8dcd95b12bc82804", size = 121195, upload-time = "2025-08-12T15:11:13.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/68/7b40b39ac2c1c644d4644e706d0de6c9999764341cd85f2a9393cb387661/orjson-3.11.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ab6e6b4e93b1573a026b6ec16fca9541354dd58e514b62c558b58554ae04307", size = 119234, upload-time = "2025-08-12T15:11:15.134Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/7c/bb6e7267cd80c19023d44d8cbc4ea4ed5429fcd4a7eb9950f50305697a28/orjson-3.11.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9cb23527efb61fb75527df55d20ee47989c4ee34e01a9c98ee9ede232abf6219", size = 392250, upload-time = "2025-08-12T15:11:16.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/f2/6730ace05583dbca7c1b406d59f4266e48cd0d360566e71482420fb849fc/orjson-3.11.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a4dd1268e4035af21b8a09e4adf2e61f87ee7bf63b86d7bb0a237ac03fad5b45", size = 134572, upload-time = "2025-08-12T15:11:18.205Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/0f/7d3e03a30d5aac0432882b539a65b8c02cb6dd4221ddb893babf09c424cc/orjson-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff8b155b145eaf5a9d94d2c476fbe18d6021de93cf36c2ae2c8c5b775763f14e", size = 123869, upload-time = "2025-08-12T15:11:19.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/80/1513265eba6d4a960f078f4b1d2bff94a571ab2d28c6f9835e03dfc65cc6/orjson-3.11.2-cp312-cp312-win32.whl", hash = "sha256:ae3bb10279d57872f9aba68c9931aa71ed3b295fa880f25e68da79e79453f46e", size = 124430, upload-time = "2025-08-12T15:11:20.914Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/61/eadf057b68a332351eeb3d89a4cc538d14f31cd8b5ec1b31a280426ccca2/orjson-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:d026e1967239ec11a2559b4146a61d13914504b396f74510a1c4d6b19dfd8732", size = 119598, upload-time = "2025-08-12T15:11:22.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/3f/7f4b783402143d965ab7e9a2fc116fdb887fe53bdce7d3523271cd106098/orjson-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:59f8d5ad08602711af9589375be98477d70e1d102645430b5a7985fdbf613b36", size = 114052, upload-time = "2025-08-12T15:11:23.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/f3/0dd6b4750eb556ae4e2c6a9cb3e219ec642e9c6d95f8ebe5dc9020c67204/orjson-3.11.2-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a079fdba7062ab396380eeedb589afb81dc6683f07f528a03b6f7aae420a0219", size = 226419, upload-time = "2025-08-12T15:11:25.517Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/d5/e67f36277f78f2af8a4690e0c54da6b34169812f807fd1b4bfc4dbcf9558/orjson-3.11.2-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:6a5f62ebbc530bb8bb4b1ead103647b395ba523559149b91a6c545f7cd4110ad", size = 115803, upload-time = "2025-08-12T15:11:27.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/37/ff8bc86e0dacc48f07c2b6e20852f230bf4435611bab65e3feae2b61f0ae/orjson-3.11.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7df6c7b8b0931feb3420b72838c3e2ba98c228f7aa60d461bc050cf4ca5f7b2", size = 111337, upload-time = "2025-08-12T15:11:28.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/25/37d4d3e8079ea9784ea1625029988e7f4594ce50d4738b0c1e2bf4a9e201/orjson-3.11.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f59dfea7da1fced6e782bb3699718088b1036cb361f36c6e4dd843c5111aefe", size = 116222, upload-time = "2025-08-12T15:11:30.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/32/a63fd9c07fce3b4193dcc1afced5dd4b0f3a24e27556604e9482b32189c9/orjson-3.11.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edf49146520fef308c31aa4c45b9925fd9c7584645caca7c0c4217d7900214ae", size = 119020, upload-time = "2025-08-12T15:11:31.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/b6/400792b8adc3079a6b5d649264a3224d6342436d9fac9a0ed4abc9dc4596/orjson-3.11.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50995bbeb5d41a32ad15e023305807f561ac5dcd9bd41a12c8d8d1d2c83e44e6", size = 120721, upload-time = "2025-08-12T15:11:33.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/f3/31ab8f8c699eb9e65af8907889a0b7fef74c1d2b23832719a35da7bb0c58/orjson-3.11.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cc42960515076eb639b705f105712b658c525863d89a1704d984b929b0577d1", size = 123574, upload-time = "2025-08-12T15:11:34.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/a6/ce4287c412dff81878f38d06d2c80845709c60012ca8daf861cb064b4574/orjson-3.11.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56777cab2a7b2a8ea687fedafb84b3d7fdafae382165c31a2adf88634c432fa", size = 121225, upload-time = "2025-08-12T15:11:36.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/b0/7a881b2aef4fed0287d2a4fbb029d01ed84fa52b4a68da82bdee5e50598e/orjson-3.11.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07349e88025b9b5c783077bf7a9f401ffbfb07fd20e86ec6fc5b7432c28c2c5e", size = 119201, upload-time = "2025-08-12T15:11:37.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/98/a325726b37f7512ed6338e5e65035c3c6505f4e628b09a5daf0419f054ea/orjson-3.11.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:45841fbb79c96441a8c58aa29ffef570c5df9af91f0f7a9572e5505e12412f15", size = 392193, upload-time = "2025-08-12T15:11:39.153Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/4f/a7194f98b0ce1d28190e0c4caa6d091a3fc8d0107ad2209f75c8ba398984/orjson-3.11.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13d8d8db6cd8d89d4d4e0f4161acbbb373a4d2a4929e862d1d2119de4aa324ac", size = 134548, upload-time = "2025-08-12T15:11:40.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/5e/b84caa2986c3f472dc56343ddb0167797a708a8d5c3be043e1e2677b55df/orjson-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51da1ee2178ed09c00d09c1b953e45846bbc16b6420965eb7a913ba209f606d8", size = 123798, upload-time = "2025-08-12T15:11:42.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/5b/e398449080ce6b4c8fcadad57e51fa16f65768e1b142ba90b23ac5d10801/orjson-3.11.2-cp313-cp313-win32.whl", hash = "sha256:51dc033df2e4a4c91c0ba4f43247de99b3cbf42ee7a42ee2b2b2f76c8b2f2cb5", size = 124402, upload-time = "2025-08-12T15:11:44.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/66/429e4608e124debfc4790bfc37131f6958e59510ba3b542d5fc163be8e5f/orjson-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:29d91d74942b7436f29b5d1ed9bcfc3f6ef2d4f7c4997616509004679936650d", size = 119498, upload-time = "2025-08-12T15:11:45.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/04/f8b5f317cce7ad3580a9ad12d7e2df0714dfa8a83328ecddd367af802f5b/orjson-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:4ca4fb5ac21cd1e48028d4f708b1bb13e39c42d45614befd2ead004a8bba8535", size = 114051, upload-time = "2025-08-12T15:11:47.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f", size = 238238, upload-time = "2025-08-26T17:44:54.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91", size = 127713, upload-time = "2025-08-26T17:44:55.596Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904", size = 123241, upload-time = "2025-08-26T17:44:57.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6", size = 127895, upload-time = "2025-08-26T17:44:58.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d", size = 130303, upload-time = "2025-08-26T17:44:59.491Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038", size = 132366, upload-time = "2025-08-26T17:45:00.654Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb", size = 135180, upload-time = "2025-08-26T17:45:02.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2", size = 132741, upload-time = "2025-08-26T17:45:03.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55", size = 131104, upload-time = "2025-08-26T17:45:04.939Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1", size = 403887, upload-time = "2025-08-26T17:45:06.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824", size = 145855, upload-time = "2025-08-26T17:45:08.338Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f", size = 135361, upload-time = "2025-08-26T17:45:09.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204", size = 136190, upload-time = "2025-08-26T17:45:10.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b", size = 131389, upload-time = "2025-08-26T17:45:12.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e", size = 126120, upload-time = "2025-08-26T17:45:13.515Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/b0/a7edab2a00cdcb2688e1c943401cb3236323e7bfd2839815c6131a3742f4/orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b", size = 238259, upload-time = "2025-08-26T17:45:15.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/c6/ff4865a9cc398a07a83342713b5932e4dc3cb4bf4bc04e8f83dedfc0d736/orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2", size = 127633, upload-time = "2025-08-26T17:45:16.417Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/e6/e00bea2d9472f44fe8794f523e548ce0ad51eb9693cf538a753a27b8bda4/orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a", size = 123061, upload-time = "2025-08-26T17:45:17.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/31/9fbb78b8e1eb3ac605467cb846e1c08d0588506028b37f4ee21f978a51d4/orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c", size = 127956, upload-time = "2025-08-26T17:45:19.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/88/b0604c22af1eed9f98d709a96302006915cfd724a7ebd27d6dd11c22d80b/orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064", size = 130790, upload-time = "2025-08-26T17:45:20.586Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/9d/1c1238ae9fffbfed51ba1e507731b3faaf6b846126a47e9649222b0fd06f/orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424", size = 132385, upload-time = "2025-08-26T17:45:22.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/b5/c06f1b090a1c875f337e21dd71943bc9d84087f7cdf8c6e9086902c34e42/orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23", size = 135305, upload-time = "2025-08-26T17:45:23.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/26/5f028c7d81ad2ebbf84414ba6d6c9cac03f22f5cd0d01eb40fb2d6a06b07/orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667", size = 132875, upload-time = "2025-08-26T17:45:25.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/d4/b8df70d9cfb56e385bf39b4e915298f9ae6c61454c8154a0f5fd7efcd42e/orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f", size = 130940, upload-time = "2025-08-26T17:45:27.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/5e/afe6a052ebc1a4741c792dd96e9f65bf3939d2094e8b356503b68d48f9f5/orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1", size = 403852, upload-time = "2025-08-26T17:45:28.478Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/90/7bbabafeb2ce65915e9247f14a56b29c9334003536009ef5b122783fe67e/orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc", size = 146293, upload-time = "2025-08-26T17:45:29.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/b3/2d703946447da8b093350570644a663df69448c9d9330e5f1d9cce997f20/orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049", size = 135470, upload-time = "2025-08-26T17:45:31.243Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/70/b14dcfae7aff0e379b0119c8a812f8396678919c431efccc8e8a0263e4d9/orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca", size = 136248, upload-time = "2025-08-26T17:45:32.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/b8/9e3127d65de7fff243f7f3e53f59a531bf6bb295ebe5db024c2503cc0726/orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1", size = 131437, upload-time = "2025-08-26T17:45:34.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/92/a946e737d4d8a7fd84a606aba96220043dcc7d6988b9e7551f7f6d5ba5ad/orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710", size = 125978, upload-time = "2025-08-26T17:45:36.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/79/8932b27293ad35919571f77cb3693b5906cf14f206ef17546052a241fdf6/orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810", size = 238127, upload-time = "2025-08-26T17:45:38.146Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/82/cb93cd8cf132cd7643b30b6c5a56a26c4e780c7a145db6f83de977b540ce/orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43", size = 127494, upload-time = "2025-08-26T17:45:39.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/b8/2d9eb181a9b6bb71463a78882bcac1027fd29cf62c38a40cc02fc11d3495/orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27", size = 123017, upload-time = "2025-08-26T17:45:40.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/14/a0e971e72d03b509190232356d54c0f34507a05050bd026b8db2bf2c192c/orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f", size = 127898, upload-time = "2025-08-26T17:45:42.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/af/dc74536722b03d65e17042cc30ae586161093e5b1f29bccda24765a6ae47/orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c", size = 130742, upload-time = "2025-08-26T17:45:43.511Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e6/7a3b63b6677bce089fe939353cda24a7679825c43a24e49f757805fc0d8a/orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be", size = 132377, upload-time = "2025-08-26T17:45:45.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/cd/ce2ab93e2e7eaf518f0fd15e3068b8c43216c8a44ed82ac2b79ce5cef72d/orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d", size = 135313, upload-time = "2025-08-26T17:45:46.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/b4/f98355eff0bd1a38454209bbc73372ce351ba29933cb3e2eba16c04b9448/orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2", size = 132908, upload-time = "2025-08-26T17:45:48.126Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/92/8f5182d7bc2a1bed46ed960b61a39af8389f0ad476120cd99e67182bfb6d/orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f", size = 130905, upload-time = "2025-08-26T17:45:49.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/60/c41ca753ce9ffe3d0f67b9b4c093bdd6e5fdb1bc53064f992f66bb99954d/orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee", size = 403812, upload-time = "2025-08-26T17:45:51.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/13/e4a4f16d71ce1868860db59092e78782c67082a8f1dc06a3788aef2b41bc/orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e", size = 146277, upload-time = "2025-08-26T17:45:52.851Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/8b/bafb7f0afef9344754a3a0597a12442f1b85a048b82108ef2c956f53babd/orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633", size = 135418, upload-time = "2025-08-26T17:45:54.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/d4/bae8e4f26afb2c23bea69d2f6d566132584d1c3a5fe89ee8c17b718cab67/orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b", size = 136216, upload-time = "2025-08-26T17:45:57.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/76/224985d9f127e121c8cad882cea55f0ebe39f97925de040b75ccd4b33999/orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae", size = 131362, upload-time = "2025-08-26T17:45:58.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/cf/0dce7a0be94bd36d1346be5067ed65ded6adb795fdbe3abd234c8d576d01/orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce", size = 125989, upload-time = "2025-08-26T17:45:59.95Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3914,11 +3973,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.8"
|
||||
version = "4.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4513,6 +4572,43 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-json-report"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-metadata" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4f/d3/765dae9712fcd68d820338908c1337e077d5fdadccd5cacf95b9b0bea278/pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de", size = 21241, upload-time = "2022-03-15T21:03:10.2Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/35/d07400c715bf8a88aa0c1ee9c9eb6050ca7fe5b39981f0eea773feeb0681/pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325", size = 13222, upload-time = "2022-03-15T21:03:08.65Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-metadata"
|
||||
version = "3.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/85/8c969f8bec4e559f8f2b958a15229a35495f5b4ce499f6b865eac54b878d/pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8", size = 9952, upload-time = "2024-02-12T19:38:44.887Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/43/7e7b2ec865caa92f67b8f0e9231a798d102724ca4c0e1f414316be1c1ef2/pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b", size = 11428, upload-time = "2024-02-12T19:38:42.531Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-mock"
|
||||
version = "3.14.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-order"
|
||||
version = "1.3.0"
|
||||
@@ -5349,6 +5445,25 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.1"
|
||||
|
||||
Reference in New Issue
Block a user