diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..89f8a2a42a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,28 @@ +# Ignore everything by default, selectively add things to context +* + +# AutoGPT +!autogpt/autogpt/ +!autogpt/pyproject.toml +!autogpt/poetry.lock +!autogpt/README.md +!autogpt/tests/ + +# Benchmark +!benchmark/agbenchmark/ +!benchmark/pyproject.toml +!benchmark/poetry.lock +!benchmark/README.md + +# Forge +!forge/forge/ +!forge/pyproject.toml +!forge/poetry.lock +!forge/README.md + +# Frontend +!frontend/build/web/ + +# Explicitly re-ignore some folders +.* +**/__pycache__ diff --git a/.gitattributes b/.gitattributes index d3adc9db4b..f18b519846 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ -frontend/build/* linguist-generated +frontend/build/** linguist-generated **/poetry.lock linguist-generated + +docs/_javascript/** linguist-vendored diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d586fc2c47..9683ddf848 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,5 @@ .github/workflows/ @Significant-Gravitas/devops -autogpts/autogpt/ @Significant-Gravitas/maintainers -autogpts/forge/ @Significant-Gravitas/forge-maintainers +autogpt/ @Significant-Gravitas/maintainers +forge/ @Significant-Gravitas/forge-maintainers benchmark/ @Significant-Gravitas/benchmark-maintainers frontend/ @Significant-Gravitas/frontend-maintainers diff --git a/.github/ISSUE_TEMPLATE/1.bug.yml b/.github/ISSUE_TEMPLATE/1.bug.yml index 3b9b6d265e..1bcff49f83 100644 --- a/.github/ISSUE_TEMPLATE/1.bug.yml +++ b/.github/ISSUE_TEMPLATE/1.bug.yml @@ -90,7 +90,7 @@ body: attributes: label: Do you use OpenAI GPT-3 or GPT-4? description: > - If you are using AutoGPT with `--gpt3only`, your problems may be caused by + If you are using AutoGPT with `SMART_LLM=gpt-3.5-turbo`, your problems may be caused by the [limitations](https://github.com/Significant-Gravitas/AutoGPT/issues?q=is%3Aissue+label%3A%22AI+model+limitation%22) of GPT-3.5. options: - GPT-3.5 diff --git a/.github/labeler.yml b/.github/labeler.yml index cef6db4320..153c485d42 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,10 +1,10 @@ AutoGPT Agent: - changed-files: - - any-glob-to-any-file: autogpts/autogpt/** + - any-glob-to-any-file: autogpt/** Forge: - changed-files: - - any-glob-to-any-file: autogpts/forge/** + - any-glob-to-any-file: forge/** Benchmark: - changed-files: diff --git a/.github/workflows/autogpt-ci.yml b/.github/workflows/autogpt-ci.yml index bc3858e88d..5a7d032ae4 100644 --- a/.github/workflows/autogpt-ci.yml +++ b/.github/workflows/autogpt-ci.yml @@ -1,18 +1,18 @@ -name: AutoGPT Python CI +name: AutoGPT CI on: push: branches: [ master, development, ci-test* ] paths: - '.github/workflows/autogpt-ci.yml' - - 'autogpts/autogpt/**' - - '!autogpts/autogpt/tests/vcr_cassettes' + - 'autogpt/**' + - '!autogpt/tests/vcr_cassettes' pull_request: branches: [ master, development, release-* ] paths: - '.github/workflows/autogpt-ci.yml' - - 'autogpts/autogpt/**' - - '!autogpts/autogpt/tests/vcr_cassettes' + - 'autogpt/**' + - '!autogpt/tests/vcr_cassettes' concurrency: group: ${{ format('autogpt-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }} @@ -21,60 +21,9 @@ concurrency: defaults: run: shell: bash - working-directory: autogpts/autogpt + working-directory: autogpt jobs: - lint: - runs-on: ubuntu-latest - env: - min-python-version: "3.10" - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python ${{ env.min-python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ env.min-python-version }} - - - id: get_date - name: Get date - run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Set up Python dependency cache - uses: actions/cache@v4 - with: - path: ~/.cache/pypoetry - key: ${{ runner.os }}-poetry-${{ hashFiles('autogpts/autogpt/pyproject.toml') }}-${{ steps.get_date.outputs.date }} - - - name: Install Python dependencies - run: | - curl -sSL https://install.python-poetry.org | python3 - - poetry install - - - name: Lint with flake8 - run: poetry run flake8 - - - name: Check black formatting - run: poetry run black . --check - if: success() || failure() - - - name: Check isort formatting - run: poetry run isort . --check - if: success() || failure() - - # - name: Check mypy formatting - # run: poetry run mypy - # if: success() || failure() - - # - name: Check for unused imports and pass statements - # run: | - # cmd="autoflake --remove-all-unused-imports --recursive --ignore-init-module-imports --ignore-pass-after-docstring autogpt tests" - # poetry run $cmd --check || (echo "You have unused imports or pass statements, please run '${cmd} --in-place'" && exit 1) - test: permissions: contents: read @@ -170,7 +119,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }} - key: poetry-${{ runner.os }}-${{ hashFiles('autogpts/autogpt/poetry.lock') }} + key: poetry-${{ runner.os }}-${{ hashFiles('autogpt/poetry.lock') }} - name: Install Poetry (Unix) if: runner.os != 'Windows' @@ -293,4 +242,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-logs - path: autogpts/autogpt/logs/ + path: autogpt/logs/ diff --git a/.github/workflows/autogpt-docker-cache-clean.yml b/.github/workflows/autogpt-docker-cache-clean.yml index 22c940128d..c665b1d735 100644 --- a/.github/workflows/autogpt-docker-cache-clean.yml +++ b/.github/workflows/autogpt-docker-cache-clean.yml @@ -25,7 +25,7 @@ jobs: name: Build image uses: docker/build-push-action@v5 with: - context: autogpts/autogpt + file: Dockerfile.autogpt build-args: BUILD_TYPE=${{ matrix.build-type }} load: true # save to docker images # use GHA cache as read-only diff --git a/.github/workflows/autogpt-docker-ci.yml b/.github/workflows/autogpt-docker-ci.yml index 4ef63547e7..7620eed638 100644 --- a/.github/workflows/autogpt-docker-ci.yml +++ b/.github/workflows/autogpt-docker-ci.yml @@ -5,14 +5,14 @@ on: branches: [ master, development ] paths: - '.github/workflows/autogpt-docker-ci.yml' - - 'autogpts/autogpt/**' - - '!autogpts/autogpt/tests/vcr_cassettes' + - 'autogpt/**' + - '!autogpt/tests/vcr_cassettes' pull_request: branches: [ master, development, release-* ] paths: - '.github/workflows/autogpt-docker-ci.yml' - - 'autogpts/autogpt/**' - - '!autogpts/autogpt/tests/vcr_cassettes' + - 'autogpt/**' + - '!autogpt/tests/vcr_cassettes' concurrency: group: ${{ format('autogpt-docker-ci-{0}', github.head_ref && format('pr-{0}', github.event.pull_request.number) || github.sha) }} @@ -20,7 +20,7 @@ concurrency: defaults: run: - working-directory: autogpts/autogpt + working-directory: autogpt env: IMAGE_NAME: auto-gpt @@ -49,7 +49,7 @@ jobs: name: Build image uses: docker/build-push-action@v5 with: - context: autogpts/autogpt + file: Dockerfile.autogpt build-args: BUILD_TYPE=${{ matrix.build-type }} tags: ${{ env.IMAGE_NAME }} labels: GIT_REVISION=${{ github.sha }} @@ -84,7 +84,6 @@ jobs: vars_json: ${{ toJSON(vars) }} run: .github/workflows/scripts/docker-ci-summary.sh >> $GITHUB_STEP_SUMMARY - working-directory: ./ continue-on-error: true test: @@ -119,7 +118,7 @@ jobs: name: Build image uses: docker/build-push-action@v5 with: - context: autogpts/autogpt + file: Dockerfile.autogpt build-args: BUILD_TYPE=dev # include pytest tags: > ${{ env.IMAGE_NAME }}, diff --git a/.github/workflows/autogpt-docker-release.yml b/.github/workflows/autogpt-docker-release.yml index f45a63a2af..7fd554f369 100644 --- a/.github/workflows/autogpt-docker-release.yml +++ b/.github/workflows/autogpt-docker-release.yml @@ -10,10 +10,6 @@ on: type: boolean description: 'Build from scratch, without using cached layers' -defaults: - run: - working-directory: autogpts/autogpt - env: IMAGE_NAME: auto-gpt DEPLOY_IMAGE_NAME: ${{ secrets.DOCKER_USER }}/auto-gpt @@ -48,7 +44,7 @@ jobs: name: Build image uses: docker/build-push-action@v5 with: - context: autogpts/autogpt + file: Dockerfile.autogpt build-args: BUILD_TYPE=release load: true # save to docker images # push: true # TODO: uncomment when this issue is fixed: https://github.com/moby/buildkit/issues/1555 @@ -87,5 +83,4 @@ jobs: vars_json: ${{ toJSON(vars) }} run: .github/workflows/scripts/docker-release-summary.sh >> $GITHUB_STEP_SUMMARY - working-directory: ./ continue-on-error: true diff --git a/.github/workflows/autogpts-benchmark.yml b/.github/workflows/autogpts-benchmark.yml index fb1cb6f08e..4698930591 100644 --- a/.github/workflows/autogpts-benchmark.yml +++ b/.github/workflows/autogpts-benchmark.yml @@ -42,7 +42,7 @@ jobs: - name: Benchmark ${{ matrix.agent-name }} run: | ./run agent start ${{ matrix.agent-name }} - cd autogpts/${{ matrix.agent-name }} + cd ${{ matrix.agent-name }} set +e # Do not quit on non-zero exit codes poetry run agbenchmark run -N 3 \ diff --git a/.github/workflows/autogpts-ci.yml b/.github/workflows/autogpts-ci.yml index 19f8c5ab28..33e4f3d5df 100644 --- a/.github/workflows/autogpts-ci.yml +++ b/.github/workflows/autogpts-ci.yml @@ -1,4 +1,4 @@ -name: AutoGPTs smoke test CI +name: Agent smoke tests on: workflow_dispatch: @@ -8,7 +8,8 @@ on: branches: [ master, development, ci-test* ] paths: - '.github/workflows/autogpts-ci.yml' - - 'autogpts/**' + - 'autogpt/**' + - 'forge/**' - 'benchmark/**' - 'run' - 'cli.py' @@ -18,7 +19,8 @@ on: branches: [ master, development, release-* ] paths: - '.github/workflows/autogpts-ci.yml' - - 'autogpts/**' + - 'autogpt/**' + - 'forge/**' - 'benchmark/**' - 'run' - 'cli.py' @@ -26,11 +28,11 @@ on: - '!**/*.md' jobs: - run-tests: + serve-agent-protocol: runs-on: ubuntu-latest strategy: matrix: - agent-name: [ autogpt, forge ] + agent-name: [ autogpt ] fail-fast: false timeout-minutes: 20 env: @@ -48,14 +50,14 @@ jobs: python-version: ${{ env.min-python-version }} - name: Install Poetry - working-directory: ./autogpts/${{ matrix.agent-name }}/ + working-directory: ./${{ matrix.agent-name }}/ run: | curl -sSL https://install.python-poetry.org | python - - name: Run regression tests run: | ./run agent start ${{ matrix.agent-name }} - cd autogpts/${{ matrix.agent-name }} + cd ${{ matrix.agent-name }} poetry run agbenchmark --mock --test=BasicRetrieval --test=Battleship --test=WebArenaTask_0 poetry run agbenchmark --test=WriteFile env: diff --git a/.github/workflows/benchmark-ci.yml b/.github/workflows/benchmark-ci.yml index 88c5750ac1..d939a8bf70 100644 --- a/.github/workflows/benchmark-ci.yml +++ b/.github/workflows/benchmark-ci.yml @@ -1,4 +1,4 @@ -name: Benchmark CI +name: AGBenchmark CI on: push: @@ -14,62 +14,91 @@ on: - '!benchmark/reports/**' - .github/workflows/benchmark-ci.yml +concurrency: + group: ${{ format('benchmark-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} + +defaults: + run: + shell: bash + env: min-python-version: '3.10' jobs: - lint: - runs-on: ubuntu-latest - + test: + permissions: + contents: read + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ["3.10"] + platform-os: [ubuntu, macos, macos-arm64, windows] + runs-on: ${{ matrix.platform-os != 'macos-arm64' && format('{0}-latest', matrix.platform-os) || 'macos-14' }} + defaults: + run: + shell: bash + working-directory: benchmark steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 + submodules: true - - name: Set up Python ${{ env.min-python-version }} + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: ${{ env.min-python-version }} + python-version: ${{ matrix.python-version }} - - id: get_date - name: Get date - working-directory: ./benchmark/ - run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + - name: Set up Python dependency cache + # On Windows, unpacking cached dependencies takes longer than just installing them + if: runner.os != 'Windows' + uses: actions/cache@v4 + with: + path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }} + key: poetry-${{ runner.os }}-${{ hashFiles('benchmark/poetry.lock') }} - - name: Install Poetry - working-directory: ./benchmark/ + - name: Install Poetry (Unix) + if: runner.os != 'Windows' run: | - curl -sSL https://install.python-poetry.org | python - + curl -sSL https://install.python-poetry.org | python3 - - - name: Install dependencies - working-directory: ./benchmark/ + if [ "${{ runner.os }}" = "macOS" ]; then + PATH="$HOME/.local/bin:$PATH" + echo "$HOME/.local/bin" >> $GITHUB_PATH + fi + + - name: Install Poetry (Windows) + if: runner.os == 'Windows' + shell: pwsh run: | - export POETRY_VIRTUALENVS_IN_PROJECT=true - poetry install -vvv + (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - - - name: Lint with flake8 - working-directory: ./benchmark/ - run: poetry run flake8 + $env:PATH += ";$env:APPDATA\Python\Scripts" + echo "$env:APPDATA\Python\Scripts" >> $env:GITHUB_PATH - - name: Check black formatting - working-directory: ./benchmark/ - run: poetry run black . --exclude test.py --check - if: success() || failure() + - name: Install Python dependencies + run: poetry install - - name: Check isort formatting - working-directory: ./benchmark/ - run: poetry run isort . --check - if: success() || failure() - - - name: Check for unused imports and pass statements - working-directory: ./benchmark/ + - name: Run pytest with coverage run: | - cmd="poetry run autoflake --remove-all-unused-imports --recursive --ignore-init-module-imports --ignore-pass-after-docstring agbenchmark" - $cmd --check || (echo "You have unused imports or pass statements, please run '${cmd} --in-place'" && exit 1) - if: success() || failure() + poetry run pytest -vv \ + --cov=agbenchmark --cov-branch --cov-report term-missing --cov-report xml \ + --durations=10 \ + tests + env: + CI: true + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - tests-agbenchmark: + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: agbenchmark,${{ runner.os }} + + self-test-with-agent: runs-on: ubuntu-latest strategy: matrix: @@ -89,14 +118,14 @@ jobs: python-version: ${{ env.min-python-version }} - name: Install Poetry - working-directory: ./autogpts/${{ matrix.agent-name }}/ run: | curl -sSL https://install.python-poetry.org | python - - name: Run regression tests + working-directory: . run: | ./run agent start ${{ matrix.agent-name }} - cd autogpts/${{ matrix.agent-name }} + cd ${{ matrix.agent-name }} set +e # Ignore non-zero exit codes and continue execution echo "Running the following command: poetry run agbenchmark --maintain --mock" @@ -119,13 +148,12 @@ jobs: echo "Running the following command: poetry run agbenchmark --test=WriteFile" poetry run agbenchmark --test=WriteFile - cd ../../benchmark + cd ../benchmark poetry install echo "Adding the BUILD_SKILL_TREE environment variable. This will attempt to add new elements in the skill tree. If new elements are added, the CI fails because they should have been pushed" export BUILD_SKILL_TREE=true poetry run agbenchmark --mock - poetry run pytest -vv -s tests CHANGED=$(git diff --name-only | grep -E '(agbenchmark/challenges)|(../frontend/assets)') || echo "No diffs" if [ ! -z "$CHANGED" ]; then diff --git a/.github/workflows/forge-ci.yml b/.github/workflows/forge-ci.yml new file mode 100644 index 0000000000..d996bdb2f8 --- /dev/null +++ b/.github/workflows/forge-ci.yml @@ -0,0 +1,129 @@ +name: Forge CI + +on: + push: + branches: [ master, development, ci-test* ] + paths: + - '.github/workflows/forge-ci.yml' + - 'forge/**' + pull_request: + branches: [ master, development, release-* ] + paths: + - '.github/workflows/forge-ci.yml' + - 'forge/**' + +concurrency: + group: ${{ format('forge-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} + +defaults: + run: + shell: bash + working-directory: forge + +jobs: + test: + permissions: + contents: read + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ["3.10"] + platform-os: [ubuntu, macos, macos-arm64, windows] + runs-on: ${{ matrix.platform-os != 'macos-arm64' && format('{0}-latest', matrix.platform-os) || 'macos-14' }} + + steps: + # Quite slow on macOS (2~4 minutes to set up Docker) + # - name: Set up Docker (macOS) + # if: runner.os == 'macOS' + # uses: crazy-max/ghaction-setup-docker@v3 + + - name: Start MinIO service (Linux) + if: runner.os == 'Linux' + working-directory: '.' + run: | + docker pull minio/minio:edge-cicd + docker run -d -p 9000:9000 minio/minio:edge-cicd + + - name: Start MinIO service (macOS) + if: runner.os == 'macOS' + working-directory: ${{ runner.temp }} + run: | + brew install minio/stable/minio + mkdir data + minio server ./data & + + # No MinIO on Windows: + # - Windows doesn't support running Linux Docker containers + # - It doesn't seem possible to start background processes on Windows. They are + # killed after the step returns. + # See: https://github.com/actions/runner/issues/598#issuecomment-2011890429 + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Set up Python dependency cache + # On Windows, unpacking cached dependencies takes longer than just installing them + if: runner.os != 'Windows' + uses: actions/cache@v4 + with: + path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }} + key: poetry-${{ runner.os }}-${{ hashFiles('forge/poetry.lock') }} + + - name: Install Poetry (Unix) + if: runner.os != 'Windows' + run: | + curl -sSL https://install.python-poetry.org | python3 - + + if [ "${{ runner.os }}" = "macOS" ]; then + PATH="$HOME/.local/bin:$PATH" + echo "$HOME/.local/bin" >> $GITHUB_PATH + fi + + - name: Install Poetry (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - + + $env:PATH += ";$env:APPDATA\Python\Scripts" + echo "$env:APPDATA\Python\Scripts" >> $env:GITHUB_PATH + + - name: Install Python dependencies + run: poetry install + + - name: Run pytest with coverage + run: | + poetry run pytest -vv \ + --cov=forge --cov-branch --cov-report term-missing --cov-report xml \ + --durations=10 \ + forge + env: + CI: true + PLAIN_OUTPUT: True + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + S3_ENDPOINT_URL: ${{ runner.os != 'Windows' && 'http://127.0.0.1:9000' || '' }} + AWS_ACCESS_KEY_ID: minioadmin + AWS_SECRET_ACCESS_KEY: minioadmin + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: forge,${{ runner.os }} + + - name: Upload logs to artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: forge/logs/ diff --git a/.github/workflows/hackathon.yml b/.github/workflows/hackathon.yml index 94b2c752f3..d165d268fe 100644 --- a/.github/workflows/hackathon.yml +++ b/.github/workflows/hackathon.yml @@ -117,7 +117,7 @@ jobs: branch=$(jq -r '.["branch_to_benchmark"]' arena/$AGENT_NAME.json) git clone "$link" -b "$branch" "$AGENT_NAME" cd $AGENT_NAME - cp ./autogpts/$AGENT_NAME/.env.example ./autogpts/$AGENT_NAME/.env || echo "file not found" + cp ./$AGENT_NAME/.env.example ./$AGENT_NAME/.env || echo "file not found" ./run agent start $AGENT_NAME cd ../benchmark poetry install diff --git a/.github/workflows/pr-label.yml b/.github/workflows/pr-label.yml index 415637702f..15b4e73a9b 100644 --- a/.github/workflows/pr-label.yml +++ b/.github/workflows/pr-label.yml @@ -5,7 +5,7 @@ on: push: branches: [ master, development, release-* ] paths-ignore: - - 'autogpts/autogpt/tests/vcr_cassettes' + - 'autogpt/tests/vcr_cassettes' - 'benchmark/reports/**' # So that the `dirtyLabel` is removed if conflicts are resolve # We recommend `pull_request_target` so that github secrets are available. diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml new file mode 100644 index 0000000000..17d49f283b --- /dev/null +++ b/.github/workflows/python-checks.yml @@ -0,0 +1,151 @@ +name: Python checks + +on: + push: + branches: [ master, development, ci-test* ] + paths: + - '.github/workflows/lint-ci.yml' + - 'autogpt/**' + - 'forge/**' + - 'benchmark/**' + - '**.py' + - '!autogpt/tests/vcr_cassettes' + pull_request: + branches: [ master, development, release-* ] + paths: + - '.github/workflows/lint-ci.yml' + - 'autogpt/**' + - 'forge/**' + - 'benchmark/**' + - '**.py' + - '!autogpt/tests/vcr_cassettes' + +concurrency: + group: ${{ format('lint-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} + +defaults: + run: + shell: bash + +jobs: + get-changed-parts: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - id: changes-in + name: Determine affected subprojects + uses: dorny/paths-filter@v3 + with: + filters: | + autogpt: + - autogpt/autogpt/** + - autogpt/tests/** + - autogpt/poetry.lock + forge: + - forge/forge/** + - forge/tests/** + - forge/poetry.lock + benchmark: + - benchmark/agbenchmark/** + - benchmark/tests/** + - benchmark/poetry.lock + outputs: + changed-parts: ${{ steps.changes-in.outputs.changes }} + + lint: + needs: get-changed-parts + runs-on: ubuntu-latest + env: + min-python-version: "3.10" + + strategy: + matrix: + sub-package: ${{ fromJson(needs.get-changed-parts.outputs.changed-parts) }} + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ env.min-python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.min-python-version }} + + - name: Set up Python dependency cache + uses: actions/cache@v4 + with: + path: ~/.cache/pypoetry + key: ${{ runner.os }}-poetry-${{ hashFiles(format('{0}/poetry.lock', matrix.sub-package)) }} + + - name: Install Poetry + run: curl -sSL https://install.python-poetry.org | python3 - + + # Install dependencies + + - name: Install Python dependencies + run: poetry -C ${{ matrix.sub-package }} install + + # Lint + + - name: Lint (isort) + run: poetry run isort --check . + working-directory: ${{ matrix.sub-package }} + + - name: Lint (Black) + if: success() || failure() + run: poetry run black --check . + working-directory: ${{ matrix.sub-package }} + + - name: Lint (Flake8) + if: success() || failure() + run: poetry run flake8 . + working-directory: ${{ matrix.sub-package }} + + types: + needs: get-changed-parts + runs-on: ubuntu-latest + env: + min-python-version: "3.10" + + strategy: + matrix: + sub-package: ${{ fromJson(needs.get-changed-parts.outputs.changed-parts) }} + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ env.min-python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.min-python-version }} + + - name: Set up Python dependency cache + uses: actions/cache@v4 + with: + path: ~/.cache/pypoetry + key: ${{ runner.os }}-poetry-${{ hashFiles(format('{0}/poetry.lock', matrix.sub-package)) }} + + - name: Install Poetry + run: curl -sSL https://install.python-poetry.org | python3 - + + # Install dependencies + + - name: Install Python dependencies + run: poetry -C ${{ matrix.sub-package }} install + + # Typecheck + + - name: Typecheck + if: success() || failure() + run: poetry run pyright + working-directory: ${{ matrix.sub-package }} diff --git a/.gitignore b/.gitignore index 090f3bdbc8..3b4050b42e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ auto_gpt_workspace/* *.mpeg .env azure.yaml -ai_settings.yaml -last_run_ai_settings.yaml .vscode .idea/* auto-gpt.json diff --git a/.gitmodules b/.gitmodules index c7d5712200..0258bcd361 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "autogpts/autogpt/tests/vcr_cassettes"] - path = autogpts/autogpt/tests/vcr_cassettes +[submodule "autogpt/tests/vcr_cassettes"] + path = autogpt/tests/vcr_cassettes url = https://github.com/Significant-Gravitas/Auto-GPT-test-cassettes diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..7f480ee551 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,127 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + args: ["--maxkb=500"] + - id: fix-byte-order-marker + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: debug-statements + + - repo: local + # isort needs the context of which packages are installed to function, so we + # can't use a vendored isort pre-commit hook (which runs in its own isolated venv). + hooks: + - id: isort-autogpt + name: Lint (isort) - AutoGPT + entry: poetry -C autogpt run isort + files: ^autogpt/ + types: [file, python] + language: system + + - id: isort-forge + name: Lint (isort) - Forge + entry: poetry -C forge run isort + files: ^forge/ + types: [file, python] + language: system + + - id: isort-benchmark + name: Lint (isort) - Benchmark + entry: poetry -C benchmark run isort + files: ^benchmark/ + types: [file, python] + language: system + + - repo: https://github.com/psf/black + rev: 23.12.1 + # Black has sensible defaults, doesn't need package context, and ignores + # everything in .gitignore, so it works fine without any config or arguments. + hooks: + - id: black + name: Lint (Black) + language_version: python3.10 + + - repo: https://github.com/PyCQA/flake8 + rev: 7.0.0 + # To have flake8 load the config of the individual subprojects, we have to call + # them separately. + hooks: + - id: flake8 + name: Lint (Flake8) - AutoGPT + alias: flake8-autogpt + files: ^autogpt/(autogpt|scripts|tests)/ + args: [--config=autogpt/.flake8] + + - id: flake8 + name: Lint (Flake8) - Forge + alias: flake8-forge + files: ^forge/(forge|tests)/ + args: [--config=forge/.flake8] + + - id: flake8 + name: Lint (Flake8) - Benchmark + alias: flake8-benchmark + files: ^benchmark/(agbenchmark|tests)/((?!reports).)*[/.] + args: [--config=benchmark/.flake8] + + - repo: local + # To have watertight type checking, we check *all* the files in an affected + # project. To trigger on poetry.lock we also reset the file `types` filter. + hooks: + - id: pyright + name: Typecheck - AutoGPT + alias: pyright-autogpt + entry: poetry -C autogpt run pyright + args: [-p, autogpt, autogpt] + # include forge source (since it's a path dependency) but exclude *_test.py files: + files: ^(autogpt/((autogpt|scripts|tests)/|poetry\.lock$)|forge/(forge/.*(? include bare minimum FROM autogpt-base as autogpt-release RUN poetry install --no-cache --no-root --without dev \ && rm -rf $(poetry env info --path)/src -ONBUILD COPY autogpt/ ./autogpt -ONBUILD COPY scripts/ ./scripts -ONBUILD COPY plugins/ ./plugins -ONBUILD COPY prompt_settings.yaml ./prompt_settings.yaml -ONBUILD COPY README.md ./README.md +ONBUILD COPY autogpt/autogpt/ ./autogpt +ONBUILD COPY autogpt/scripts/ ./scripts +ONBUILD COPY autogpt/plugins/ ./plugins +ONBUILD COPY autogpt/README.md ./README.md ONBUILD RUN mkdir ./data FROM autogpt-${BUILD_TYPE} AS autogpt diff --git a/QUICKSTART.md b/QUICKSTART.md index dbdde3be21..b72ba4994e 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -95,9 +95,9 @@ Examples: `SwiftyosAssistant`, `PwutsPRAgent`, `MySuperAgent` ## Running your Agent -Your agent can started using the `./run agent start YOUR_AGENT_NAME` +Your agent can be started using the command: `./run agent start YOUR_AGENT_NAME` -This start the agent on `http://localhost:8000/` +This starts the agent on the URL: `http://localhost:8000/` ![Start the Agent](docs/content/imgs/quickstart/009_start_agent.png) diff --git a/README.md b/README.md index b47934734c..c254583ae5 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,12 @@ Be part of the revolution! **AutoGPT** is here to stay, at the forefront of AI i ### 🏗️ Forge -**Forge your own agent!** – Forge is a ready-to-go template for your agent application. All the boilerplate code is already handled, letting you channel all your creativity into the things that set *your* agent apart. All tutorials are located [here](https://medium.com/@aiedge/autogpt-forge-e3de53cc58ec). Components from the [`forge.sdk`](/autogpts/forge/forge/sdk) can also be used individually to speed up development and reduce boilerplate in your agent project. +**Forge your own agent!** – Forge is a ready-to-go template for your agent application. All the boilerplate code is already handled, letting you channel all your creativity into the things that set *your* agent apart. All tutorials are located [here](https://medium.com/@aiedge/autogpt-forge-e3de53cc58ec). Components from the [`forge.sdk`](/forge/forge/sdk) can also be used individually to speed up development and reduce boilerplate in your agent project. -🚀 [**Getting Started with Forge**](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/forge/tutorials/001_getting_started.md) – +🚀 [**Getting Started with Forge**](https://github.com/Significant-Gravitas/AutoGPT/blob/master/forge/tutorials/001_getting_started.md) – This guide will walk you through the process of creating your own agent and using the benchmark and user interface. -📘 [Learn More](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/forge) about Forge +📘 [Learn More](https://github.com/Significant-Gravitas/AutoGPT/tree/master/forge) about Forge ### 🎯 Benchmark @@ -43,7 +43,7 @@ This guide will walk you through the process of creating your own agent and usin **Makes agents easy to use!** The `frontend` gives you a user-friendly interface to control and monitor your agents. It connects to agents through the [agent protocol](#-agent-protocol), ensuring compatibility with many agents from both inside and outside of our ecosystem. - + The frontend works out-of-the-box with all agents in the repo. Just use the [CLI] to run your agent of choice! diff --git a/autogpts/autogpt/.coveragerc b/autogpt/.coveragerc similarity index 100% rename from autogpts/autogpt/.coveragerc rename to autogpt/.coveragerc diff --git a/autogpts/autogpt/.devcontainer/Dockerfile b/autogpt/.devcontainer/Dockerfile similarity index 100% rename from autogpts/autogpt/.devcontainer/Dockerfile rename to autogpt/.devcontainer/Dockerfile diff --git a/autogpts/autogpt/.devcontainer/devcontainer.json b/autogpt/.devcontainer/devcontainer.json similarity index 100% rename from autogpts/autogpt/.devcontainer/devcontainer.json rename to autogpt/.devcontainer/devcontainer.json diff --git a/autogpts/autogpt/.devcontainer/docker-compose.yml b/autogpt/.devcontainer/docker-compose.yml similarity index 100% rename from autogpts/autogpt/.devcontainer/docker-compose.yml rename to autogpt/.devcontainer/docker-compose.yml diff --git a/autogpts/autogpt/.env.template b/autogpt/.env.template similarity index 96% rename from autogpts/autogpt/.env.template rename to autogpt/.env.template index 14c7bcaa5d..5653780d9b 100644 --- a/autogpts/autogpt/.env.template +++ b/autogpt/.env.template @@ -8,6 +8,9 @@ ## ANTHROPIC_API_KEY - Anthropic API Key (Example: sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) # ANTHROPIC_API_KEY= +## GROQ_API_KEY - Groq API Key (Example: gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) +# GROQ_API_KEY= + ## TELEMETRY_OPT_IN - Share telemetry on errors and other issues with the AutoGPT team, e.g. through Sentry. ## This helps us to spot and solve problems earlier & faster. (Default: DISABLED) # TELEMETRY_OPT_IN=true @@ -44,12 +47,6 @@ ## USER_AGENT - Define the user-agent used by the requests library to browse website (string) # USER_AGENT="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36" -## AI_SETTINGS_FILE - Specifies which AI Settings file to use, relative to the AutoGPT root directory. (defaults to ai_settings.yaml) -# AI_SETTINGS_FILE=ai_settings.yaml - -## PROMPT_SETTINGS_FILE - Specifies which Prompt Settings file to use, relative to the AutoGPT root directory. (defaults to prompt_settings.yaml) -# PROMPT_SETTINGS_FILE=prompt_settings.yaml - ## AUTHORISE COMMAND KEY - Key to authorise commands # AUTHORISE_COMMAND_KEY=y diff --git a/autogpts/autogpt/.envrc b/autogpt/.envrc similarity index 100% rename from autogpts/autogpt/.envrc rename to autogpt/.envrc diff --git a/autogpt/.flake8 b/autogpt/.flake8 new file mode 100644 index 0000000000..fcdb70526c --- /dev/null +++ b/autogpt/.flake8 @@ -0,0 +1,14 @@ +[flake8] +max-line-length = 88 +# Ignore rules that conflict with Black code style +extend-ignore = E203, W503 +exclude = + .git, + __pycache__/, + *.pyc, + .pytest_cache/, + venv*/, + .venv/, + data/, + logs/, + tests/unit/data/, diff --git a/autogpts/autogpt/.gitattributes b/autogpt/.gitattributes similarity index 100% rename from autogpts/autogpt/.gitattributes rename to autogpt/.gitattributes diff --git a/autogpts/autogpt/.gitignore b/autogpt/.gitignore similarity index 98% rename from autogpts/autogpt/.gitignore rename to autogpt/.gitignore index aaff758a03..80ecf24ed4 100644 --- a/autogpts/autogpt/.gitignore +++ b/autogpt/.gitignore @@ -4,8 +4,6 @@ autogpt/*.json *.mpeg .env azure.yaml -ai_settings.yaml -last_run_ai_settings.yaml .vscode .idea/* auto-gpt.json diff --git a/autogpts/autogpt/.sourcery.yaml b/autogpt/.sourcery.yaml similarity index 100% rename from autogpts/autogpt/.sourcery.yaml rename to autogpt/.sourcery.yaml diff --git a/autogpts/autogpt/BULLETIN.md b/autogpt/BULLETIN.md similarity index 100% rename from autogpts/autogpt/BULLETIN.md rename to autogpt/BULLETIN.md diff --git a/autogpts/autogpt/README.md b/autogpt/README.md similarity index 91% rename from autogpts/autogpt/README.md rename to autogpt/README.md index 86e0aa69fa..c6cd20af6c 100644 --- a/autogpts/autogpt/README.md +++ b/autogpt/README.md @@ -64,18 +64,10 @@ Options: -c, --continuous Enable Continuous Mode -y, --skip-reprompt Skips the re-prompting messages at the beginning of the script - -C, --ai-settings FILE Specifies which ai_settings.yaml file to - use, relative to the AutoGPT root directory. - Will also automatically skip the re-prompt. - -P, --prompt-settings FILE Specifies which prompt_settings.yaml file to - use. -l, --continuous-limit INTEGER Defines the number of times to run in continuous mode --speak Enable Speak Mode --debug Enable Debug Mode - --gpt3only Enable GPT3.5 Only Mode - --gpt4only Enable GPT4 Only Mode - -m, --use-memory TEXT Defines which Memory backend to use -b, --browser-name TEXT Specifies which web-browser to use when using selenium to scrape the web. --allow-downloads Dangerous: Allows AutoGPT to download files @@ -118,12 +110,7 @@ Usage: python -m autogpt serve [OPTIONS] agent for every task. Options: - -P, --prompt-settings FILE Specifies which prompt_settings.yaml file to - use. --debug Enable Debug Mode - --gpt3only Enable GPT3.5 Only Mode - --gpt4only Enable GPT4 Only Mode - -m, --use-memory TEXT Defines which Memory backend to use -b, --browser-name TEXT Specifies which web-browser to use when using selenium to scrape the web. --allow-downloads Dangerous: Allows AutoGPT to download files diff --git a/autogpts/autogpt/agbenchmark_config/.gitignore b/autogpt/agbenchmark_config/.gitignore similarity index 100% rename from autogpts/autogpt/agbenchmark_config/.gitignore rename to autogpt/agbenchmark_config/.gitignore diff --git a/autogpts/autogpt/agbenchmark_config/__init__.py b/autogpt/agbenchmark_config/__init__.py similarity index 100% rename from autogpts/autogpt/agbenchmark_config/__init__.py rename to autogpt/agbenchmark_config/__init__.py diff --git a/autogpts/autogpt/agbenchmark_config/analyze_reports.py b/autogpt/agbenchmark_config/analyze_reports.py similarity index 100% rename from autogpts/autogpt/agbenchmark_config/analyze_reports.py rename to autogpt/agbenchmark_config/analyze_reports.py diff --git a/autogpts/autogpt/agbenchmark_config/config.json b/autogpt/agbenchmark_config/config.json similarity index 100% rename from autogpts/autogpt/agbenchmark_config/config.json rename to autogpt/agbenchmark_config/config.json diff --git a/autogpts/autogpt/autogpt.bat b/autogpt/autogpt.bat similarity index 100% rename from autogpts/autogpt/autogpt.bat rename to autogpt/autogpt.bat diff --git a/autogpts/autogpt/autogpt.sh b/autogpt/autogpt.sh similarity index 100% rename from autogpts/autogpt/autogpt.sh rename to autogpt/autogpt.sh diff --git a/autogpts/autogpt/autogpt/__init__.py b/autogpt/autogpt/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/__init__.py rename to autogpt/autogpt/__init__.py diff --git a/autogpts/autogpt/autogpt/__main__.py b/autogpt/autogpt/__main__.py similarity index 100% rename from autogpts/autogpt/autogpt/__main__.py rename to autogpt/autogpt/__main__.py diff --git a/autogpts/autogpt/autogpt/agent_factory/configurators.py b/autogpt/autogpt/agent_factory/configurators.py similarity index 79% rename from autogpts/autogpt/autogpt/agent_factory/configurators.py rename to autogpt/autogpt/agent_factory/configurators.py index 18b63a01c1..1e930e8eab 100644 --- a/autogpts/autogpt/autogpt/agent_factory/configurators.py +++ b/autogpt/autogpt/agent_factory/configurators.py @@ -4,7 +4,7 @@ from forge.config.ai_directives import AIDirectives from forge.config.ai_profile import AIProfile from forge.config.config import Config from forge.file_storage.base import FileStorage -from forge.llm.providers import ChatModelProvider +from forge.llm.providers import MultiProvider from autogpt.agents.agent import Agent, AgentConfiguration, AgentSettings @@ -12,16 +12,16 @@ from autogpt.agents.agent import Agent, AgentConfiguration, AgentSettings def create_agent( agent_id: str, task: str, - ai_profile: AIProfile, app_config: Config, file_storage: FileStorage, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, + ai_profile: Optional[AIProfile] = None, directives: Optional[AIDirectives] = None, ) -> Agent: if not task: raise ValueError("No task specified for new agent") - if not directives: - directives = AIDirectives.from_file(app_config.prompt_settings_file) + ai_profile = ai_profile or AIProfile() + directives = directives or AIDirectives() agent = _configure_agent( agent_id=agent_id, @@ -40,7 +40,7 @@ def configure_agent_with_state( state: AgentSettings, app_config: Config, file_storage: FileStorage, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, ) -> Agent: return _configure_agent( state=state, @@ -52,7 +52,7 @@ def configure_agent_with_state( def _configure_agent( app_config: Config, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, file_storage: FileStorage, agent_id: str = "", task: str = "", @@ -60,22 +60,22 @@ def _configure_agent( directives: Optional[AIDirectives] = None, state: Optional[AgentSettings] = None, ) -> Agent: - if not (state or agent_id and task and ai_profile and directives): + if state: + agent_state = state + elif agent_id and task and ai_profile and directives: + agent_state = state or create_agent_state( + agent_id=agent_id, + task=task, + ai_profile=ai_profile, + directives=directives, + app_config=app_config, + ) + else: raise TypeError( "Either (state) or (agent_id, task, ai_profile, directives)" " must be specified" ) - agent_state = state or create_agent_state( - agent_id=agent_id, - task=task, - ai_profile=ai_profile, - directives=directives, - app_config=app_config, - ) - - # TODO: configure memory - return Agent( settings=agent_state, llm_provider=llm_provider, diff --git a/autogpts/autogpt/autogpt/agent_factory/generators.py b/autogpt/autogpt/agent_factory/generators.py similarity index 75% rename from autogpts/autogpt/autogpt/agent_factory/generators.py rename to autogpt/autogpt/agent_factory/generators.py index 3b60dfa309..3f1f55e713 100644 --- a/autogpts/autogpt/autogpt/agent_factory/generators.py +++ b/autogpt/autogpt/agent_factory/generators.py @@ -2,13 +2,12 @@ from __future__ import annotations from typing import TYPE_CHECKING -from forge.config.ai_directives import AIDirectives from forge.file_storage.base import FileStorage if TYPE_CHECKING: from autogpt.agents.agent import Agent from forge.config.config import Config - from forge.llm.providers.schema import ChatModelProvider + from forge.llm.providers import MultiProvider from .configurators import _configure_agent from .profile_generator import generate_agent_profile_for_task @@ -19,9 +18,8 @@ async def generate_agent_for_task( task: str, app_config: Config, file_storage: FileStorage, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, ) -> Agent: - base_directives = AIDirectives.from_file(app_config.prompt_settings_file) ai_profile, task_directives = await generate_agent_profile_for_task( task=task, app_config=app_config, @@ -31,7 +29,7 @@ async def generate_agent_for_task( agent_id=agent_id, task=task, ai_profile=ai_profile, - directives=base_directives + task_directives, + directives=task_directives, app_config=app_config, file_storage=file_storage, llm_provider=llm_provider, diff --git a/autogpts/autogpt/autogpt/agent_factory/profile_generator.py b/autogpt/autogpt/agent_factory/profile_generator.py similarity index 72% rename from autogpts/autogpt/autogpt/agent_factory/profile_generator.py rename to autogpt/autogpt/agent_factory/profile_generator.py index 32851194ce..604438fd79 100644 --- a/autogpts/autogpt/autogpt/agent_factory/profile_generator.py +++ b/autogpt/autogpt/agent_factory/profile_generator.py @@ -5,10 +5,10 @@ from forge.config.ai_directives import AIDirectives from forge.config.ai_profile import AIProfile from forge.config.config import Config from forge.llm.prompting import ChatPrompt, LanguageModelClassification, PromptStrategy +from forge.llm.providers import MultiProvider from forge.llm.providers.schema import ( AssistantChatMessage, ChatMessage, - ChatModelProvider, CompletionModelFunction, ) from forge.models.config import SystemConfiguration, UserConfigurable @@ -21,53 +21,48 @@ class AgentProfileGeneratorConfiguration(SystemConfiguration): model_classification: LanguageModelClassification = UserConfigurable( default=LanguageModelClassification.SMART_MODEL ) - _example_call: object = [ - { - "type": "function", - "function": { - "name": "create_agent", - "arguments": { - "name": "CMOGPT", - "description": ( - "a professional digital marketer AI that assists Solopreneurs " - "in growing their businesses by providing " - "world-class expertise in solving marketing problems " - "for SaaS, content products, agencies, and more." + _example_call: object = { + "name": "create_agent", + "arguments": { + "name": "CMOGPT", + "description": ( + "a professional digital marketer AI that assists Solopreneurs " + "in growing their businesses by providing " + "world-class expertise in solving marketing problems " + "for SaaS, content products, agencies, and more." + ), + "directives": { + "best_practices": [ + ( + "Engage in effective problem-solving, prioritization, " + "planning, and supporting execution to address your " + "marketing needs as your virtual " + "Chief Marketing Officer." ), - "directives": { - "best_practices": [ - ( - "Engage in effective problem-solving, prioritization, " - "planning, and supporting execution to address your " - "marketing needs as your virtual " - "Chief Marketing Officer." - ), - ( - "Provide specific, actionable, and concise advice to " - "help you make informed decisions without the use of " - "platitudes or overly wordy explanations." - ), - ( - "Identify and prioritize quick wins and cost-effective " - "campaigns that maximize results with minimal time and " - "budget investment." - ), - ( - "Proactively take the lead in guiding you and offering " - "suggestions when faced with unclear information or " - "uncertainty to ensure your marketing strategy remains " - "on track." - ), - ], - "constraints": [ - "Do not suggest illegal or unethical plans or strategies.", - "Take reasonable budgetary limits into account.", - ], - }, - }, + ( + "Provide specific, actionable, and concise advice to " + "help you make informed decisions without the use of " + "platitudes or overly wordy explanations." + ), + ( + "Identify and prioritize quick wins and cost-effective " + "campaigns that maximize results with minimal time and " + "budget investment." + ), + ( + "Proactively take the lead in guiding you and offering " + "suggestions when faced with unclear information or " + "uncertainty to ensure your marketing strategy remains " + "on track." + ), + ], + "constraints": [ + "Do not suggest illegal or unethical plans or strategies.", + "Take reasonable budgetary limits into account.", + ], }, - } - ] + }, + } system_prompt: str = UserConfigurable( default=( "Your job is to respond to a user-defined task, given in triple quotes, by " @@ -141,7 +136,7 @@ class AgentProfileGeneratorConfiguration(SystemConfiguration): required=True, ), }, - ).schema + ).dict() ) @@ -160,7 +155,7 @@ class AgentProfileGenerator(PromptStrategy): self._model_classification = model_classification self._system_prompt_message = system_prompt self._user_prompt_template = user_prompt_template - self._create_agent_function = CompletionModelFunction.parse( + self._create_agent_function = CompletionModelFunction.parse_obj( create_agent_function ) @@ -183,7 +178,7 @@ class AgentProfileGenerator(PromptStrategy): def parse_response_content( self, - response_content: AssistantChatMessage, + response: AssistantChatMessage, ) -> tuple[AIProfile, AIDirectives]: """Parse the actual text response from the objective model. @@ -192,18 +187,17 @@ class AgentProfileGenerator(PromptStrategy): Returns: The parsed response. - """ try: - if not response_content.tool_calls: + if not response.tool_calls: raise ValueError( f"LLM did not call {self._create_agent_function.name} function; " "agent profile creation failed" ) - arguments: object = response_content.tool_calls[0].function.arguments + arguments: object = response.tool_calls[0].function.arguments ai_profile = AIProfile( - ai_name=arguments.get("name"), - ai_role=arguments.get("description"), + ai_name=arguments.get("name"), # type: ignore + ai_role=arguments.get("description"), # type: ignore ) ai_directives = AIDirectives( best_practices=arguments.get("directives", {}).get("best_practices"), @@ -211,7 +205,7 @@ class AgentProfileGenerator(PromptStrategy): resources=[], ) except KeyError: - logger.debug(f"Failed to parse this response content: {response_content}") + logger.debug(f"Failed to parse this response content: {response}") raise return ai_profile, ai_directives @@ -219,7 +213,7 @@ class AgentProfileGenerator(PromptStrategy): async def generate_agent_profile_for_task( task: str, app_config: Config, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, ) -> tuple[AIProfile, AIDirectives]: """Generates an AIConfig object from the given string. diff --git a/autogpts/autogpt/autogpt/agents/README.md b/autogpt/autogpt/agents/README.md similarity index 92% rename from autogpts/autogpt/autogpt/agents/README.md rename to autogpt/autogpt/agents/README.md index 4ab5732431..c7d14d311a 100644 --- a/autogpts/autogpt/autogpt/agents/README.md +++ b/autogpt/autogpt/agents/README.md @@ -24,7 +24,7 @@ class MyAgent(Agent): def __init__( self, settings: AgentSettings, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider file_storage: FileStorage, legacy_config: Config, ): @@ -34,4 +34,4 @@ class MyAgent(Agent): self.my_component = MyComponent() ``` -For more customization, you can override the `propose_action` and `execute` or even subclass `BaseAgent` directly. This way you can have full control over the agent's components and behavior. Have a look at the [implementation of Agent](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/autogpt/agents/agent.py) for more details. +For more customization, you can override the `propose_action` and `execute` or even subclass `BaseAgent` directly. This way you can have full control over the agent's components and behavior. Have a look at the [implementation of Agent](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/autogpt/agents/agent.py) for more details. diff --git a/autogpts/autogpt/autogpt/agents/__init__.py b/autogpt/autogpt/agents/__init__.py similarity index 71% rename from autogpts/autogpt/autogpt/agents/__init__.py rename to autogpt/autogpt/agents/__init__.py index 1ee5800d70..3abf44be0d 100644 --- a/autogpts/autogpt/autogpt/agents/__init__.py +++ b/autogpt/autogpt/agents/__init__.py @@ -1,7 +1,9 @@ from .agent import Agent +from .agent_manager import AgentManager from .prompt_strategies.one_shot import OneShotAgentActionProposal __all__ = [ + "AgentManager", "Agent", "OneShotAgentActionProposal", ] diff --git a/autogpts/autogpt/autogpt/agents/agent.py b/autogpt/autogpt/agents/agent.py similarity index 94% rename from autogpts/autogpt/autogpt/agents/agent.py rename to autogpt/autogpt/agents/agent.py index 57088da317..72c4191425 100644 --- a/autogpts/autogpt/autogpt/agents/agent.py +++ b/autogpt/autogpt/agents/agent.py @@ -3,7 +3,7 @@ from __future__ import annotations import inspect import logging from datetime import datetime -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Any, ClassVar, Optional import sentry_sdk from forge.agent.base import BaseAgent, BaseAgentConfiguration, BaseAgentSettings @@ -14,7 +14,7 @@ from forge.agent.protocols import ( DirectiveProvider, MessageProvider, ) -from forge.command.command import Command, CommandOutput +from forge.command.command import Command from forge.components.action_history import ( ActionHistoryComponent, EpisodicActionHistory, @@ -34,11 +34,12 @@ from forge.components.web import WebSearchComponent, WebSeleniumComponent from forge.file_storage.base import FileStorage from forge.llm.prompting import PromptStrategy from forge.llm.prompting.schema import ChatPrompt +from forge.llm.prompting.utils import dump_prompt from forge.llm.providers import ( AssistantFunctionCall, ChatMessage, - ChatModelProvider, ChatModelResponse, + MultiProvider, ) from forge.llm.providers.utils import function_specs_from_commands from forge.models.action import ( @@ -62,7 +63,6 @@ from autogpt.app.log_cycle import ( USER_INPUT_FILE_NAME, LogCycleHandler, ) -from autogpt.core.runner.client_lib.logging.helpers import dump_prompt from .prompt_strategies.code_flow import CodeFlowAgentPromptStrategy from .prompt_strategies.one_shot import OneShotAgentActionProposal @@ -78,7 +78,9 @@ class AgentConfiguration(BaseAgentConfiguration): class AgentSettings(BaseAgentSettings): - config: AgentConfiguration = Field(default_factory=AgentConfiguration) + config: AgentConfiguration = Field( # type: ignore + default_factory=AgentConfiguration + ) history: EpisodicActionHistory[OneShotAgentActionProposal] = Field( default_factory=EpisodicActionHistory[OneShotAgentActionProposal] @@ -88,8 +90,8 @@ class AgentSettings(BaseAgentSettings): context: AgentContext = Field(default_factory=AgentContext) -class Agent(BaseAgent, Configurable[AgentSettings]): - default_settings: AgentSettings = AgentSettings( +class Agent(BaseAgent[OneShotAgentActionProposal], Configurable[AgentSettings]): + default_settings: ClassVar[AgentSettings] = AgentSettings( name="Agent", description=__doc__ if __doc__ else "", ) @@ -97,7 +99,7 @@ class Agent(BaseAgent, Configurable[AgentSettings]): def __init__( self, settings: AgentSettings, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, file_storage: FileStorage, legacy_config: Config, prompt_strategy_class: type[PromptStrategy] = CodeFlowAgentPromptStrategy, @@ -105,8 +107,6 @@ class Agent(BaseAgent, Configurable[AgentSettings]): super().__init__(settings) self.llm_provider = llm_provider - self.ai_profile = settings.ai_profile - self.directives = settings.directives prompt_config = prompt_strategy_class.default_configuration.copy(deep=True) prompt_config.use_functions_api = ( settings.config.use_functions_api @@ -117,7 +117,7 @@ class Agent(BaseAgent, Configurable[AgentSettings]): self.commands: list[Command] = [] # Components - self.system = SystemComponent(legacy_config, settings.ai_profile) + self.system = SystemComponent() self.history = ActionHistoryComponent( settings.history, self.send_token_limit, @@ -286,7 +286,7 @@ class Agent(BaseAgent, Configurable[AgentSettings]): return result - async def _execute_tool(self, tool_call: AssistantFunctionCall) -> CommandOutput: + async def _execute_tool(self, tool_call: AssistantFunctionCall) -> Any: """Execute the command and return the result Args: diff --git a/autogpts/autogpt/autogpt/agent_manager/agent_manager.py b/autogpt/autogpt/agents/agent_manager.py similarity index 100% rename from autogpts/autogpt/autogpt/agent_manager/agent_manager.py rename to autogpt/autogpt/agents/agent_manager.py diff --git a/autogpts/autogpt/autogpt/agents/prompt_strategies/code_flow.py b/autogpt/autogpt/agents/prompt_strategies/code_flow.py similarity index 100% rename from autogpts/autogpt/autogpt/agents/prompt_strategies/code_flow.py rename to autogpt/autogpt/agents/prompt_strategies/code_flow.py diff --git a/autogpts/autogpt/autogpt/agents/prompt_strategies/one_shot.py b/autogpt/autogpt/agents/prompt_strategies/one_shot.py similarity index 91% rename from autogpts/autogpt/autogpt/agents/prompt_strategies/one_shot.py rename to autogpt/autogpt/agents/prompt_strategies/one_shot.py index 5e6b76f731..a6cf5d16c5 100644 --- a/autogpts/autogpt/autogpt/agents/prompt_strategies/one_shot.py +++ b/autogpt/autogpt/agents/prompt_strategies/one_shot.py @@ -50,7 +50,7 @@ class AssistantThoughts(ModelWithSummary): class OneShotAgentActionProposal(ActionProposal): - thoughts: AssistantThoughts + thoughts: AssistantThoughts # type: ignore class OneShotAgentPromptConfiguration(SystemConfiguration): @@ -167,10 +167,7 @@ class OneShotAgentPromptStrategy(PromptStrategy): + (self._generate_os_info() if include_os_info else []) + [ self.config.body_template.format( - constraints=format_numbered_list( - ai_directives.constraints - + self._generate_budget_constraint(ai_profile.api_budget) - ), + constraints=format_numbered_list(ai_directives.constraints), resources=format_numbered_list(ai_directives.resources), commands=self._generate_commands_list(commands), best_practices=format_numbered_list(ai_directives.best_practices), @@ -193,11 +190,8 @@ class OneShotAgentPromptStrategy(PromptStrategy): def response_format_instruction(self, use_functions_api: bool) -> tuple[str, str]: response_schema = self.response_schema.copy(deep=True) - if ( - use_functions_api - and response_schema.properties - and "use_tool" in response_schema.properties - ): + assert response_schema.properties + if use_functions_api and "use_tool" in response_schema.properties: del response_schema.properties["use_tool"] # Unindent for performance @@ -247,19 +241,6 @@ class OneShotAgentPromptStrategy(PromptStrategy): ) return [f"The OS you are running on is: {os_info}"] - def _generate_budget_constraint(self, api_budget: float) -> list[str]: - """Generates the budget information part of the prompt. - - Returns: - list[str]: The budget information part of the prompt, or an empty list. - """ - if api_budget > 0.0: - return [ - f"It takes money to let you run. " - f"Your API budget is ${api_budget:.3f}" - ] - return [] - def _generate_commands_list(self, commands: list[CompletionModelFunction]) -> str: """Lists the commands available to the agent. @@ -295,10 +276,10 @@ class OneShotAgentPromptStrategy(PromptStrategy): "Parsing object extracted from LLM response:\n" f"{json.dumps(assistant_reply_dict, indent=4)}" ) - - parsed_response = OneShotAgentActionProposal.parse_obj(assistant_reply_dict) if self.config.use_functions_api: if not response.tool_calls: raise InvalidAgentResponseError("Assistant did not use a tool") - parsed_response.use_tool = response.tool_calls[0].function + assistant_reply_dict["use_tool"] = response.tool_calls[0].function + + parsed_response = OneShotAgentActionProposal.parse_obj(assistant_reply_dict) return parsed_response diff --git a/autogpts/autogpt/autogpt/app/__init__.py b/autogpt/autogpt/app/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/app/__init__.py rename to autogpt/autogpt/app/__init__.py diff --git a/autogpts/autogpt/autogpt/app/agent_protocol_server.py b/autogpt/autogpt/app/agent_protocol_server.py similarity index 95% rename from autogpts/autogpt/autogpt/app/agent_protocol_server.py rename to autogpt/autogpt/app/agent_protocol_server.py index 48cdb00f52..7544d817b2 100644 --- a/autogpts/autogpt/autogpt/app/agent_protocol_server.py +++ b/autogpt/autogpt/app/agent_protocol_server.py @@ -10,13 +10,10 @@ from fastapi import APIRouter, FastAPI, UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse, StreamingResponse from fastapi.staticfiles import StaticFiles -from forge.config.config import Config -from forge.file_storage import FileStorage -from forge.llm.providers import ChatModelProvider, ModelProviderBudget -from forge.models.action import ActionErrorResult, ActionSuccessResult -from forge.sdk.db import AgentDB -from forge.sdk.middlewares import AgentMiddleware -from forge.sdk.model import ( +from forge.agent_protocol.api_router import base_router +from forge.agent_protocol.database import AgentDB +from forge.agent_protocol.middlewares import AgentMiddleware +from forge.agent_protocol.models import ( Artifact, Step, StepRequestBody, @@ -26,16 +23,18 @@ from forge.sdk.model import ( TaskRequestBody, TaskStepsListResponse, ) -from forge.sdk.routes.agent_protocol import base_router +from forge.config.config import Config +from forge.file_storage import FileStorage +from forge.llm.providers import ModelProviderBudget, MultiProvider +from forge.models.action import ActionErrorResult, ActionSuccessResult from forge.utils.const import ASK_COMMAND, FINISH_COMMAND from forge.utils.exceptions import AgentFinished, NotFoundError from hypercorn.asyncio import serve as hypercorn_serve from hypercorn.config import Config as HypercornConfig from sentry_sdk import set_user -from autogpt.agent_factory.configurators import configure_agent_with_state -from autogpt.agent_factory.generators import generate_agent_for_task -from autogpt.agent_manager import AgentManager +from autogpt.agent_factory.configurators import configure_agent_with_state, create_agent +from autogpt.agents.agent_manager import AgentManager from autogpt.app.utils import is_port_free logger = logging.getLogger(__name__) @@ -49,7 +48,7 @@ class AgentProtocolServer: app_config: Config, database: AgentDB, file_storage: FileStorage, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, ): self.app_config = app_config self.db = database @@ -98,9 +97,7 @@ class AgentProtocolServer: app.include_router(router, prefix="/ap/v1") script_dir = os.path.dirname(os.path.realpath(__file__)) frontend_path = ( - pathlib.Path(script_dir) - .joinpath("../../../../frontend/build/web") - .resolve() + pathlib.Path(script_dir).joinpath("../../../frontend/build/web").resolve() ) if os.path.exists(frontend_path): @@ -123,7 +120,7 @@ class AgentProtocolServer: config.bind = [f"0.0.0.0:{port}"] logger.info(f"AutoGPT server starting on http://localhost:{port}") - await hypercorn_serve(app, config) + await hypercorn_serve(app, config) # type: ignore async def create_task(self, task_request: TaskRequestBody) -> Task: """ @@ -136,8 +133,10 @@ class AgentProtocolServer: input=task_request.input, additional_input=task_request.additional_input, ) - logger.debug(f"Creating agent for task: '{task.input}'") - task_agent = await generate_agent_for_task( + # TODO: re-evaluate performance benefit of task-oriented profiles + # logger.debug(f"Creating agent for task: '{task.input}'") + # task_agent = await generate_agent_for_task( + task_agent = create_agent( agent_id=task_agent_id(task.task_id), task=task.input, app_config=self.app_config, @@ -446,9 +445,7 @@ class AgentProtocolServer: agent_id = task_agent_id(task_id) return self.file_storage.clone_with_subroot(f"agents/{agent_id}/workspace") - def _get_task_llm_provider( - self, task: Task, step_id: str = "" - ) -> ChatModelProvider: + def _get_task_llm_provider(self, task: Task, step_id: str = "") -> MultiProvider: """ Configures the LLM provider with headers to link outgoing requests to the task. """ diff --git a/autogpts/autogpt/autogpt/app/cli.py b/autogpt/autogpt/app/cli.py similarity index 83% rename from autogpts/autogpt/autogpt/app/cli.py rename to autogpt/autogpt/app/cli.py index 0ff35f5a43..6c13b15174 100644 --- a/autogpts/autogpt/autogpt/app/cli.py +++ b/autogpt/autogpt/app/cli.py @@ -28,8 +28,6 @@ def cli(ctx: click.Context): help="Defines the number of times to run in continuous mode", ) @click.option("--speak", is_flag=True, help="Enable Speak Mode") -@click.option("--gpt3only", is_flag=True, help="Enable GPT3.5 Only Mode") -@click.option("--gpt4only", is_flag=True, help="Enable GPT4 Only Mode") @click.option( "-b", "--browser-name", @@ -64,15 +62,6 @@ def cli(ctx: click.Context): is_flag=True, help="Skips the re-prompting messages at the beginning of the script", ) -@click.option( - "--ai-settings", - "-C", - type=click.Path(exists=True, dir_okay=False, path_type=Path), - help=( - "Specifies which ai_settings.yaml file to use, relative to the AutoGPT" - " root directory. Will also automatically skip the re-prompt." - ), -) @click.option( "--ai-name", type=str, @@ -83,12 +72,6 @@ def cli(ctx: click.Context): type=str, help="AI role override", ) -@click.option( - "--prompt-settings", - "-P", - type=click.Path(exists=True, dir_okay=False, path_type=Path), - help="Specifies which prompt_settings.yaml file to use.", -) @click.option( "--constraint", type=str, @@ -149,18 +132,14 @@ def run( continuous: bool, continuous_limit: Optional[int], speak: bool, - gpt3only: bool, - gpt4only: bool, browser_name: Optional[str], allow_downloads: bool, workspace_directory: Optional[Path], install_plugin_deps: bool, skip_news: bool, skip_reprompt: bool, - ai_settings: Optional[Path], ai_name: Optional[str], ai_role: Optional[str], - prompt_settings: Optional[Path], resource: tuple[str], constraint: tuple[str], best_practice: tuple[str], @@ -180,16 +159,12 @@ def run( run_auto_gpt( continuous=continuous, continuous_limit=continuous_limit, - ai_settings=ai_settings, - prompt_settings=prompt_settings, skip_reprompt=skip_reprompt, speak=speak, debug=debug, log_level=log_level, log_format=log_format, log_file_format=log_file_format, - gpt3only=gpt3only, - gpt4only=gpt4only, browser_name=browser_name, allow_downloads=allow_downloads, skip_news=skip_news, @@ -205,14 +180,6 @@ def run( @cli.command() -@click.option( - "--prompt-settings", - "-P", - type=click.Path(exists=True, dir_okay=False, path_type=Path), - help="Specifies which prompt_settings.yaml file to use.", -) -@click.option("--gpt3only", is_flag=True, help="Enable GPT3.5 Only Mode") -@click.option("--gpt4only", is_flag=True, help="Enable GPT4 Only Mode") @click.option( "-b", "--browser-name", @@ -250,9 +217,6 @@ def run( type=click.Choice([i.value for i in LogFormatName]), ) def serve( - prompt_settings: Optional[Path], - gpt3only: bool, - gpt4only: bool, browser_name: Optional[str], allow_downloads: bool, install_plugin_deps: bool, @@ -269,13 +233,10 @@ def serve( from autogpt.app.main import run_auto_gpt_server run_auto_gpt_server( - prompt_settings=prompt_settings, debug=debug, log_level=log_level, log_format=log_format, log_file_format=log_file_format, - gpt3only=gpt3only, - gpt4only=gpt4only, browser_name=browser_name, allow_downloads=allow_downloads, install_plugin_deps=install_plugin_deps, diff --git a/autogpts/autogpt/autogpt/app/configurator.py b/autogpt/autogpt/app/configurator.py similarity index 53% rename from autogpts/autogpt/autogpt/app/configurator.py rename to autogpt/autogpt/app/configurator.py index 5589953b2b..1e02abca95 100644 --- a/autogpts/autogpt/autogpt/app/configurator.py +++ b/autogpt/autogpt/app/configurator.py @@ -2,17 +2,12 @@ from __future__ import annotations import logging -from pathlib import Path from typing import Literal, Optional import click -from colorama import Back, Fore, Style -from forge.config.config import GPT_3_MODEL, GPT_4_MODEL, Config +from colorama import Back, Style +from forge.config.config import GPT_3_MODEL, Config from forge.llm.providers import ModelName, MultiProvider -from forge.logging.helpers import request_user_double_check -from forge.utils.yaml_validator import validate_yaml_file - -from autogpt.memory.vector import get_supported_memory_backends logger = logging.getLogger(__name__) @@ -21,12 +16,7 @@ async def apply_overrides_to_config( config: Config, continuous: bool = False, continuous_limit: Optional[int] = None, - ai_settings_file: Optional[Path] = None, - prompt_settings_file: Optional[Path] = None, skip_reprompt: bool = False, - gpt3only: bool = False, - gpt4only: bool = False, - memory_type: Optional[str] = None, browser_name: Optional[str] = None, allow_downloads: bool = False, skip_news: bool = False, @@ -37,17 +27,12 @@ async def apply_overrides_to_config( config (Config): The config object to update. continuous (bool): Whether to run in continuous mode. continuous_limit (int): The number of times to run in continuous mode. - ai_settings_file (Path): The path to the ai_settings.yaml file. - prompt_settings_file (Path): The path to the prompt_settings.yaml file. skip_reprompt (bool): Whether to skip the re-prompting messages on start. speak (bool): Whether to enable speak mode. debug (bool): Whether to enable debug mode. log_level (int): The global log level for the application. log_format (str): The format for the log(s). log_file_format (str): Override the format for the log file. - gpt3only (bool): Whether to enable GPT3.5 only mode. - gpt4only (bool): Whether to enable GPT4 only mode. - memory_type (str): The type of memory backend to use. browser_name (str): The name of the browser to use for scraping the web. allow_downloads (bool): Whether to allow AutoGPT to download files natively. skips_news (bool): Whether to suppress the output of latest news on startup. @@ -69,64 +54,13 @@ async def apply_overrides_to_config( if continuous_limit and not continuous: raise click.UsageError("--continuous-limit can only be used with --continuous") - # Set the default LLM models - if gpt3only: - # --gpt3only should always use gpt-3.5-turbo, despite user's FAST_LLM config - config.fast_llm = GPT_3_MODEL - config.smart_llm = GPT_3_MODEL - elif ( - gpt4only - and (await check_model(GPT_4_MODEL, model_type="smart_llm")) == GPT_4_MODEL - ): - # --gpt4only should always use gpt-4, despite user's SMART_LLM config - config.fast_llm = GPT_4_MODEL - config.smart_llm = GPT_4_MODEL - else: - config.fast_llm = await check_model(config.fast_llm, "fast_llm") - config.smart_llm = await check_model(config.smart_llm, "smart_llm") - - if memory_type: - supported_memory = get_supported_memory_backends() - chosen = memory_type - if chosen not in supported_memory: - logger.warning( - extra={ - "title": "ONLY THE FOLLOWING MEMORY BACKENDS ARE SUPPORTED:", - "title_color": Fore.RED, - }, - msg=f"{supported_memory}", - ) - else: - config.memory_backend = chosen + # Check availability of configured LLMs; fallback to other LLM if unavailable + config.fast_llm = await check_model(config.fast_llm, "fast_llm") + config.smart_llm = await check_model(config.smart_llm, "smart_llm") if skip_reprompt: config.skip_reprompt = True - if ai_settings_file: - file = ai_settings_file - - # Validate file - (validated, message) = validate_yaml_file(file) - if not validated: - logger.fatal(extra={"title": "FAILED FILE VALIDATION:"}, msg=message) - request_user_double_check() - exit(1) - - config.ai_settings_file = config.project_root / file - config.skip_reprompt = True - - if prompt_settings_file: - file = prompt_settings_file - - # Validate file - (validated, message) = validate_yaml_file(file) - if not validated: - logger.fatal(extra={"title": "FAILED FILE VALIDATION:"}, msg=message) - request_user_double_check() - exit(1) - - config.prompt_settings_file = config.project_root / file - if browser_name: config.selenium_web_browser = browser_name @@ -153,7 +87,7 @@ async def check_model( ) -> ModelName: """Check if model is available for use. If not, return gpt-3.5-turbo.""" multi_provider = MultiProvider() - models = await multi_provider.get_available_models() + models = await multi_provider.get_available_chat_models() if any(model_name == m.name for m in models): return model_name diff --git a/autogpts/autogpt/autogpt/app/input.py b/autogpt/autogpt/app/input.py similarity index 100% rename from autogpts/autogpt/autogpt/app/input.py rename to autogpt/autogpt/app/input.py diff --git a/autogpts/autogpt/autogpt/app/log_cycle.py b/autogpt/autogpt/app/log_cycle.py similarity index 100% rename from autogpts/autogpt/autogpt/app/log_cycle.py rename to autogpt/autogpt/app/log_cycle.py diff --git a/autogpts/autogpt/autogpt/app/main.py b/autogpt/autogpt/app/main.py similarity index 93% rename from autogpts/autogpt/autogpt/app/main.py rename to autogpt/autogpt/app/main.py index 66135f8855..0642afb485 100644 --- a/autogpts/autogpt/autogpt/app/main.py +++ b/autogpt/autogpt/app/main.py @@ -14,28 +14,26 @@ from types import FrameType from typing import TYPE_CHECKING, Optional from colorama import Fore, Style -from forge.components.code_executor import ( +from forge.agent_protocol.database import AgentDB +from forge.components.code_executor.code_executor import ( is_docker_available, we_are_running_in_a_docker_container, ) from forge.config.ai_directives import AIDirectives from forge.config.ai_profile import AIProfile from forge.config.config import Config, ConfigBuilder, assert_config_has_openai_api_key -from forge.db import AgentDB from forge.file_storage import FileStorageBackendName, get_storage from forge.llm.providers import MultiProvider from forge.logging.config import configure_logging -from forge.logging.helpers import print_attribute, speak +from forge.logging.utils import print_attribute, speak from forge.models.action import ActionInterruptedByHuman, ActionProposal from forge.models.utils import ModelWithSummary from forge.utils.const import FINISH_COMMAND from forge.utils.exceptions import AgentTerminated, InvalidAgentResponseError from autogpt.agent_factory.configurators import configure_agent_with_state, create_agent -from autogpt.agent_factory.profile_generator import generate_agent_profile_for_task -from autogpt.agent_manager import AgentManager +from autogpt.agents.agent_manager import AgentManager from autogpt.agents.prompt_strategies.one_shot import AssistantThoughts -from autogpt.core.runner.client_lib.utils import coroutine if TYPE_CHECKING: from autogpt.agents.agent import Agent @@ -45,6 +43,7 @@ from .input import clean_input from .setup import apply_overrides_to_ai_settings, interactively_revise_ai_settings from .spinner import Spinner from .utils import ( + coroutine, get_legal_warning, markdown_to_ansi_style, print_git_branch_info, @@ -57,16 +56,12 @@ from .utils import ( async def run_auto_gpt( continuous: bool = False, continuous_limit: Optional[int] = None, - ai_settings: Optional[Path] = None, - prompt_settings: Optional[Path] = None, skip_reprompt: bool = False, speak: bool = False, debug: bool = False, log_level: Optional[str] = None, log_format: Optional[str] = None, log_file_format: Optional[str] = None, - gpt3only: bool = False, - gpt4only: bool = False, browser_name: Optional[str] = None, allow_downloads: bool = False, skip_news: bool = False, @@ -85,7 +80,9 @@ async def run_auto_gpt( local = config.file_storage_backend == FileStorageBackendName.LOCAL restrict_to_root = not local or config.restrict_to_workspace file_storage = get_storage( - config.file_storage_backend, root_path="data", restrict_to_root=restrict_to_root + config.file_storage_backend, + root_path=Path("data"), + restrict_to_root=restrict_to_root, ) file_storage.initialize() @@ -108,11 +105,7 @@ async def run_auto_gpt( config=config, continuous=continuous, continuous_limit=continuous_limit, - ai_settings_file=ai_settings, - prompt_settings_file=prompt_settings, skip_reprompt=skip_reprompt, - gpt3only=gpt3only, - gpt4only=gpt4only, browser_name=browser_name, allow_downloads=allow_downloads, skip_news=skip_news, @@ -134,7 +127,7 @@ async def run_auto_gpt( ) if not config.skip_news: - print_motd(config, logger) + print_motd(logger) print_git_branch_info(logger) print_python_version_info(logger) print_attribute("Smart LLM", config.smart_llm) @@ -146,10 +139,6 @@ async def run_auto_gpt( print_attribute("Continuous Limit", config.continuous_limit) if config.tts_config.speak_mode: print_attribute("Speak Mode", "ENABLED") - if ai_settings: - print_attribute("Using AI Settings File", ai_settings) - if prompt_settings: - print_attribute("Using Prompt Settings File", prompt_settings) if config.allow_downloads: print_attribute("Native Downloading", "ENABLED") if we_are_running_in_a_docker_container() or is_docker_available(): @@ -267,17 +256,11 @@ async def run_auto_gpt( " with as much detail as possible:", ) - base_ai_directives = AIDirectives.from_file(config.prompt_settings_file) - - ai_profile, task_oriented_ai_directives = await generate_agent_profile_for_task( - task, - app_config=config, - llm_provider=llm_provider, - ) - ai_directives = base_ai_directives + task_oriented_ai_directives + ai_profile = AIProfile() + additional_ai_directives = AIDirectives() apply_overrides_to_ai_settings( ai_profile=ai_profile, - directives=ai_directives, + directives=additional_ai_directives, override_name=override_ai_name, override_role=override_ai_role, resources=resources, @@ -297,9 +280,12 @@ async def run_auto_gpt( best_practices, ] ): - ai_profile, ai_directives = await interactively_revise_ai_settings( + ( + ai_profile, + additional_ai_directives, + ) = await interactively_revise_ai_settings( ai_profile=ai_profile, - directives=ai_directives, + directives=additional_ai_directives, app_config=config, ) else: @@ -309,7 +295,7 @@ async def run_auto_gpt( agent_id=agent_manager.generate_id(ai_profile.ai_name), task=task, ai_profile=ai_profile, - directives=ai_directives, + directives=additional_ai_directives, app_config=config, file_storage=file_storage, llm_provider=llm_provider, @@ -325,6 +311,22 @@ async def run_auto_gpt( extra={"preserve_color": True}, ) + # TODO: re-evaluate performance benefit of task-oriented profiles + # # Concurrently generate a custom profile for the agent and apply it once done + # def update_agent_directives( + # task: asyncio.Task[tuple[AIProfile, AIDirectives]] + # ): + # logger.debug(f"Updating AIProfile: {task.result()[0]}") + # logger.debug(f"Adding AIDirectives: {task.result()[1]}") + # agent.state.ai_profile = task.result()[0] + # agent.state.directives = agent.state.directives + task.result()[1] + + # asyncio.create_task( + # generate_agent_profile_for_task( + # task, app_config=config, llm_provider=llm_provider + # ) + # ).add_done_callback(update_agent_directives) + ################# # Run the Agent # ################# @@ -347,13 +349,10 @@ async def run_auto_gpt( @coroutine async def run_auto_gpt_server( - prompt_settings: Optional[Path] = None, debug: bool = False, log_level: Optional[str] = None, log_format: Optional[str] = None, log_file_format: Optional[str] = None, - gpt3only: bool = False, - gpt4only: bool = False, browser_name: Optional[str] = None, allow_downloads: bool = False, install_plugin_deps: bool = False, @@ -365,7 +364,9 @@ async def run_auto_gpt_server( local = config.file_storage_backend == FileStorageBackendName.LOCAL restrict_to_root = not local or config.restrict_to_workspace file_storage = get_storage( - config.file_storage_backend, root_path="data", restrict_to_root=restrict_to_root + config.file_storage_backend, + root_path=Path("data"), + restrict_to_root=restrict_to_root, ) file_storage.initialize() @@ -384,9 +385,6 @@ async def run_auto_gpt_server( await apply_overrides_to_config( config=config, - prompt_settings_file=prompt_settings, - gpt3only=gpt3only, - gpt4only=gpt4only, browser_name=browser_name, allow_downloads=allow_downloads, ) @@ -454,7 +452,7 @@ async def run_interaction_loop( """ # These contain both application config and agent config, so grab them here. legacy_config = agent.legacy_config - ai_profile = agent.ai_profile + ai_profile = agent.state.ai_profile logger = logging.getLogger(__name__) cycle_budget = cycles_remaining = _get_cycle_budget( diff --git a/autogpts/autogpt/autogpt/app/setup.py b/autogpt/autogpt/app/setup.py similarity index 99% rename from autogpts/autogpt/autogpt/app/setup.py rename to autogpt/autogpt/app/setup.py index b70dba7047..c95477e995 100644 --- a/autogpts/autogpt/autogpt/app/setup.py +++ b/autogpt/autogpt/app/setup.py @@ -5,7 +5,7 @@ from typing import Optional from forge.config.ai_directives import AIDirectives from forge.config.ai_profile import AIProfile from forge.config.config import Config -from forge.logging.helpers import print_attribute +from forge.logging.utils import print_attribute from .input import clean_input diff --git a/autogpts/autogpt/autogpt/app/spinner.py b/autogpt/autogpt/app/spinner.py similarity index 100% rename from autogpts/autogpt/autogpt/app/spinner.py rename to autogpt/autogpt/app/spinner.py diff --git a/autogpts/autogpt/autogpt/app/telemetry.py b/autogpt/autogpt/app/telemetry.py similarity index 100% rename from autogpts/autogpt/autogpt/app/telemetry.py rename to autogpt/autogpt/app/telemetry.py diff --git a/autogpts/autogpt/autogpt/app/utils.py b/autogpt/autogpt/app/utils.py similarity index 93% rename from autogpts/autogpt/autogpt/app/utils.py rename to autogpt/autogpt/app/utils.py index e27aa5d1c3..9ec38169a9 100644 --- a/autogpts/autogpt/autogpt/app/utils.py +++ b/autogpt/autogpt/app/utils.py @@ -1,18 +1,20 @@ +import asyncio import contextlib +import functools import logging import os import re import socket import sys from pathlib import Path -from typing import TYPE_CHECKING +from typing import Any, Callable, Coroutine, ParamSpec, TypeVar, cast import requests from colorama import Fore, Style from git import InvalidGitRepositoryError, Repo -if TYPE_CHECKING: - from forge.config.config import Config +P = ParamSpec("P") +T = TypeVar("T") logger = logging.getLogger(__name__) @@ -20,7 +22,7 @@ logger = logging.getLogger(__name__) def get_bulletin_from_web(): try: response = requests.get( - "https://raw.githubusercontent.com/Significant-Gravitas/AutoGPT/master/autogpts/autogpt/BULLETIN.md" # noqa: E501 + "https://raw.githubusercontent.com/Significant-Gravitas/AutoGPT/master/autogpt/BULLETIN.md" # noqa: E501 ) if response.status_code == 200: return response.text @@ -43,7 +45,7 @@ def vcs_state_diverges_from_master() -> bool: """ Returns whether a git repo is present and contains changes that are not in `master`. """ - paths_we_care_about = "autogpts/autogpt/autogpt/**/*.py" + paths_we_care_about = "autogpt/autogpt/**/*.py" try: repo = Repo(search_parent_directories=True) @@ -86,7 +88,7 @@ def vcs_state_diverges_from_master() -> bool: def get_git_user_email() -> str: try: repo = Repo(search_parent_directories=True) - return repo.config_reader().get_value("user", "email", default="") + return cast(str, repo.config_reader().get_value("user", "email", default="")) except InvalidGitRepositoryError: return "" @@ -159,7 +161,7 @@ By using the System, you agree to indemnify, defend, and hold harmless the Proje return legal_text -def print_motd(config: "Config", logger: logging.Logger): +def print_motd(logger: logging.Logger): motd, is_new_motd = get_latest_bulletin() if motd: motd = markdown_to_ansi_style(motd) @@ -235,3 +237,11 @@ def is_port_free(port: int, host: str = "127.0.0.1"): return True # If successful, the port is free except OSError: return False # If failed, the port is likely in use + + +def coroutine(f: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, T]: + @functools.wraps(f) + def wrapper(*args: P.args, **kwargs: P.kwargs): + return asyncio.run(f(*args, **kwargs)) + + return wrapper diff --git a/autogpts/autogpt/azure.yaml.template b/autogpt/azure.yaml.template similarity index 100% rename from autogpts/autogpt/azure.yaml.template rename to autogpt/azure.yaml.template diff --git a/autogpts/autogpt/codecov.yml b/autogpt/codecov.yml similarity index 100% rename from autogpts/autogpt/codecov.yml rename to autogpt/codecov.yml diff --git a/autogpts/autogpt/data/.keep b/autogpt/data/.keep similarity index 100% rename from autogpts/autogpt/data/.keep rename to autogpt/data/.keep diff --git a/autogpts/autogpt/docker-compose.yml b/autogpt/docker-compose.yml similarity index 78% rename from autogpts/autogpt/docker-compose.yml rename to autogpt/docker-compose.yml index 281c8f6976..0782375e5c 100644 --- a/autogpts/autogpt/docker-compose.yml +++ b/autogpt/docker-compose.yml @@ -6,20 +6,24 @@ version: "3.9" services: auto-gpt: - build: ./ + build: + context: ../ + dockerfile: Dockerfile.autogpt env_file: - .env ports: - "8000:8000" volumes: - - ./:/app + - ./:/app/autogpt/ - ./docker-compose.yml:/app/docker-compose.yml:ro - - ./Dockerfile:/app/Dockerfile:ro + # - ./Dockerfile:/app/Dockerfile:ro profiles: ["exclude-from-up"] # Only for TESTING purposes. Run with: docker compose run --build --rm autogpt-test autogpt-test: - build: ./ + build: + context: ../ + dockerfile: Dockerfile.autogpt env_file: - .env environment: @@ -29,8 +33,8 @@ services: entrypoint: ["poetry", "run"] command: ["pytest", "-v"] volumes: - - ./autogpt:/app/autogpt - - ./tests:/app/tests + - ./autogpt:/app/autogpt/autogpt + - ./tests:/app/autogpt/tests depends_on: - minio profiles: ["exclude-from-up"] diff --git a/autogpts/autogpt/hooks/post-checkout b/autogpt/hooks/post-checkout similarity index 100% rename from autogpts/autogpt/hooks/post-checkout rename to autogpt/hooks/post-checkout diff --git a/autogpts/autogpt/hooks/post-rewrite b/autogpt/hooks/post-rewrite similarity index 100% rename from autogpts/autogpt/hooks/post-rewrite rename to autogpt/hooks/post-rewrite diff --git a/autogpts/autogpt/plugin.png b/autogpt/plugin.png similarity index 100% rename from autogpts/autogpt/plugin.png rename to autogpt/plugin.png diff --git a/autogpts/autogpt/plugins/.keep b/autogpt/plugins/.keep similarity index 100% rename from autogpts/autogpt/plugins/.keep rename to autogpt/plugins/.keep diff --git a/autogpts/autogpt/poetry.lock b/autogpt/poetry.lock similarity index 91% rename from autogpts/autogpt/poetry.lock rename to autogpt/poetry.lock index c57c78edc6..67fed38824 100644 --- a/autogpts/autogpt/poetry.lock +++ b/autogpt/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "agbenchmark" @@ -38,7 +38,7 @@ uvicorn = "^0.23.2" [package.source] type = "directory" -url = "../../benchmark" +url = "../benchmark" [[package]] name = "agent-protocol-client" @@ -329,7 +329,10 @@ duckduckgo-search = "^5.0.0" fastapi = "^0.109.1" gitpython = "^3.1.32" google-api-python-client = "*" +google-cloud-logging = "^3.8.0" google-cloud-storage = "^2.13.0" +groq = "^0.8.0" +gTTS = "^2.3.1" jinja2 = "^3.1.2" jsonschema = "*" litellm = "^1.17.9" @@ -349,13 +352,14 @@ sentry-sdk = "^1.40.4" spacy = "^3.0.0" sqlalchemy = "^2.0.19" tenacity = "^8.2.2" -tiktoken = "^0.5.0" +tiktoken = ">=0.7.0,<1.0.0" toml = "^0.10.2" uvicorn = "^0.23.2" +watchdog = "4.0.0" webdriver-manager = "^4.0.1" [package.extras] -benchmark = ["agbenchmark @ file:///Users/majdyz/Code/AutoGPT/benchmark"] +benchmark = ["agbenchmark @ file:///home/reinier/code/agpt/Auto-GPT/benchmark"] [package.source] type = "directory" @@ -541,405 +545,6 @@ s3transfer = ">=0.10.0,<0.11.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] -[[package]] -name = "boto3-stubs" -version = "1.34.20" -description = "Type annotations for boto3 1.34.20 generated with mypy-boto3-builder 7.23.1" -optional = false -python-versions = ">=3.8" -files = [ - {file = "boto3-stubs-1.34.20.tar.gz", hash = "sha256:431851ea82b8596f8fa49de7014554ae0f2fa2cc6a0954459787a3b938796c68"}, - {file = "boto3_stubs-1.34.20-py3-none-any.whl", hash = "sha256:718b4503625fc5c2cada3b8e78fa10273dfb07359261be6fc07b89e2b6e03cb0"}, -] - -[package.dependencies] -botocore-stubs = "*" -mypy-boto3-s3 = {version = ">=1.34.0,<1.35.0", optional = true, markers = "extra == \"s3\""} -types-s3transfer = "*" -typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} - -[package.extras] -accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.34.0,<1.35.0)"] -account = ["mypy-boto3-account (>=1.34.0,<1.35.0)"] -acm = ["mypy-boto3-acm (>=1.34.0,<1.35.0)"] -acm-pca = ["mypy-boto3-acm-pca (>=1.34.0,<1.35.0)"] -alexaforbusiness = ["mypy-boto3-alexaforbusiness (>=1.34.0,<1.35.0)"] -all = ["mypy-boto3-accessanalyzer (>=1.34.0,<1.35.0)", "mypy-boto3-account (>=1.34.0,<1.35.0)", "mypy-boto3-acm (>=1.34.0,<1.35.0)", "mypy-boto3-acm-pca (>=1.34.0,<1.35.0)", "mypy-boto3-alexaforbusiness (>=1.34.0,<1.35.0)", "mypy-boto3-amp (>=1.34.0,<1.35.0)", "mypy-boto3-amplify (>=1.34.0,<1.35.0)", "mypy-boto3-amplifybackend (>=1.34.0,<1.35.0)", "mypy-boto3-amplifyuibuilder (>=1.34.0,<1.35.0)", "mypy-boto3-apigateway (>=1.34.0,<1.35.0)", "mypy-boto3-apigatewaymanagementapi (>=1.34.0,<1.35.0)", "mypy-boto3-apigatewayv2 (>=1.34.0,<1.35.0)", "mypy-boto3-appconfig (>=1.34.0,<1.35.0)", "mypy-boto3-appconfigdata (>=1.34.0,<1.35.0)", "mypy-boto3-appfabric (>=1.34.0,<1.35.0)", "mypy-boto3-appflow (>=1.34.0,<1.35.0)", "mypy-boto3-appintegrations (>=1.34.0,<1.35.0)", "mypy-boto3-application-autoscaling (>=1.34.0,<1.35.0)", "mypy-boto3-application-insights (>=1.34.0,<1.35.0)", "mypy-boto3-applicationcostprofiler (>=1.34.0,<1.35.0)", "mypy-boto3-appmesh (>=1.34.0,<1.35.0)", "mypy-boto3-apprunner (>=1.34.0,<1.35.0)", "mypy-boto3-appstream (>=1.34.0,<1.35.0)", "mypy-boto3-appsync (>=1.34.0,<1.35.0)", "mypy-boto3-arc-zonal-shift (>=1.34.0,<1.35.0)", "mypy-boto3-athena (>=1.34.0,<1.35.0)", "mypy-boto3-auditmanager (>=1.34.0,<1.35.0)", "mypy-boto3-autoscaling (>=1.34.0,<1.35.0)", "mypy-boto3-autoscaling-plans (>=1.34.0,<1.35.0)", "mypy-boto3-b2bi (>=1.34.0,<1.35.0)", "mypy-boto3-backup (>=1.34.0,<1.35.0)", "mypy-boto3-backup-gateway (>=1.34.0,<1.35.0)", "mypy-boto3-backupstorage (>=1.34.0,<1.35.0)", "mypy-boto3-batch (>=1.34.0,<1.35.0)", "mypy-boto3-bcm-data-exports (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-agent (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-agent-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-billingconductor (>=1.34.0,<1.35.0)", "mypy-boto3-braket (>=1.34.0,<1.35.0)", "mypy-boto3-budgets (>=1.34.0,<1.35.0)", "mypy-boto3-ce (>=1.34.0,<1.35.0)", "mypy-boto3-chime (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-identity (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-meetings (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-messaging (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-voice (>=1.34.0,<1.35.0)", "mypy-boto3-cleanrooms (>=1.34.0,<1.35.0)", "mypy-boto3-cleanroomsml (>=1.34.0,<1.35.0)", "mypy-boto3-cloud9 (>=1.34.0,<1.35.0)", "mypy-boto3-cloudcontrol (>=1.34.0,<1.35.0)", "mypy-boto3-clouddirectory (>=1.34.0,<1.35.0)", "mypy-boto3-cloudformation (>=1.34.0,<1.35.0)", "mypy-boto3-cloudfront (>=1.34.0,<1.35.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.34.0,<1.35.0)", "mypy-boto3-cloudhsm (>=1.34.0,<1.35.0)", "mypy-boto3-cloudhsmv2 (>=1.34.0,<1.35.0)", "mypy-boto3-cloudsearch (>=1.34.0,<1.35.0)", "mypy-boto3-cloudsearchdomain (>=1.34.0,<1.35.0)", "mypy-boto3-cloudtrail (>=1.34.0,<1.35.0)", "mypy-boto3-cloudtrail-data (>=1.34.0,<1.35.0)", "mypy-boto3-cloudwatch (>=1.34.0,<1.35.0)", "mypy-boto3-codeartifact (>=1.34.0,<1.35.0)", "mypy-boto3-codebuild (>=1.34.0,<1.35.0)", "mypy-boto3-codecatalyst (>=1.34.0,<1.35.0)", "mypy-boto3-codecommit (>=1.34.0,<1.35.0)", "mypy-boto3-codedeploy (>=1.34.0,<1.35.0)", "mypy-boto3-codeguru-reviewer (>=1.34.0,<1.35.0)", "mypy-boto3-codeguru-security (>=1.34.0,<1.35.0)", "mypy-boto3-codeguruprofiler (>=1.34.0,<1.35.0)", "mypy-boto3-codepipeline (>=1.34.0,<1.35.0)", "mypy-boto3-codestar (>=1.34.0,<1.35.0)", "mypy-boto3-codestar-connections (>=1.34.0,<1.35.0)", "mypy-boto3-codestar-notifications (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-identity (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-idp (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-sync (>=1.34.0,<1.35.0)", "mypy-boto3-comprehend (>=1.34.0,<1.35.0)", "mypy-boto3-comprehendmedical (>=1.34.0,<1.35.0)", "mypy-boto3-compute-optimizer (>=1.34.0,<1.35.0)", "mypy-boto3-config (>=1.34.0,<1.35.0)", "mypy-boto3-connect (>=1.34.0,<1.35.0)", "mypy-boto3-connect-contact-lens (>=1.34.0,<1.35.0)", "mypy-boto3-connectcampaigns (>=1.34.0,<1.35.0)", "mypy-boto3-connectcases (>=1.34.0,<1.35.0)", "mypy-boto3-connectparticipant (>=1.34.0,<1.35.0)", "mypy-boto3-controltower (>=1.34.0,<1.35.0)", "mypy-boto3-cost-optimization-hub (>=1.34.0,<1.35.0)", "mypy-boto3-cur (>=1.34.0,<1.35.0)", "mypy-boto3-customer-profiles (>=1.34.0,<1.35.0)", "mypy-boto3-databrew (>=1.34.0,<1.35.0)", "mypy-boto3-dataexchange (>=1.34.0,<1.35.0)", "mypy-boto3-datapipeline (>=1.34.0,<1.35.0)", "mypy-boto3-datasync (>=1.34.0,<1.35.0)", "mypy-boto3-datazone (>=1.34.0,<1.35.0)", "mypy-boto3-dax (>=1.34.0,<1.35.0)", "mypy-boto3-detective (>=1.34.0,<1.35.0)", "mypy-boto3-devicefarm (>=1.34.0,<1.35.0)", "mypy-boto3-devops-guru (>=1.34.0,<1.35.0)", "mypy-boto3-directconnect (>=1.34.0,<1.35.0)", "mypy-boto3-discovery (>=1.34.0,<1.35.0)", "mypy-boto3-dlm (>=1.34.0,<1.35.0)", "mypy-boto3-dms (>=1.34.0,<1.35.0)", "mypy-boto3-docdb (>=1.34.0,<1.35.0)", "mypy-boto3-docdb-elastic (>=1.34.0,<1.35.0)", "mypy-boto3-drs (>=1.34.0,<1.35.0)", "mypy-boto3-ds (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodb (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodbstreams (>=1.34.0,<1.35.0)", "mypy-boto3-ebs (>=1.34.0,<1.35.0)", "mypy-boto3-ec2 (>=1.34.0,<1.35.0)", "mypy-boto3-ec2-instance-connect (>=1.34.0,<1.35.0)", "mypy-boto3-ecr (>=1.34.0,<1.35.0)", "mypy-boto3-ecr-public (>=1.34.0,<1.35.0)", "mypy-boto3-ecs (>=1.34.0,<1.35.0)", "mypy-boto3-efs (>=1.34.0,<1.35.0)", "mypy-boto3-eks (>=1.34.0,<1.35.0)", "mypy-boto3-eks-auth (>=1.34.0,<1.35.0)", "mypy-boto3-elastic-inference (>=1.34.0,<1.35.0)", "mypy-boto3-elasticache (>=1.34.0,<1.35.0)", "mypy-boto3-elasticbeanstalk (>=1.34.0,<1.35.0)", "mypy-boto3-elastictranscoder (>=1.34.0,<1.35.0)", "mypy-boto3-elb (>=1.34.0,<1.35.0)", "mypy-boto3-elbv2 (>=1.34.0,<1.35.0)", "mypy-boto3-emr (>=1.34.0,<1.35.0)", "mypy-boto3-emr-containers (>=1.34.0,<1.35.0)", "mypy-boto3-emr-serverless (>=1.34.0,<1.35.0)", "mypy-boto3-entityresolution (>=1.34.0,<1.35.0)", "mypy-boto3-es (>=1.34.0,<1.35.0)", "mypy-boto3-events (>=1.34.0,<1.35.0)", "mypy-boto3-evidently (>=1.34.0,<1.35.0)", "mypy-boto3-finspace (>=1.34.0,<1.35.0)", "mypy-boto3-finspace-data (>=1.34.0,<1.35.0)", "mypy-boto3-firehose (>=1.34.0,<1.35.0)", "mypy-boto3-fis (>=1.34.0,<1.35.0)", "mypy-boto3-fms (>=1.34.0,<1.35.0)", "mypy-boto3-forecast (>=1.34.0,<1.35.0)", "mypy-boto3-forecastquery (>=1.34.0,<1.35.0)", "mypy-boto3-frauddetector (>=1.34.0,<1.35.0)", "mypy-boto3-freetier (>=1.34.0,<1.35.0)", "mypy-boto3-fsx (>=1.34.0,<1.35.0)", "mypy-boto3-gamelift (>=1.34.0,<1.35.0)", "mypy-boto3-glacier (>=1.34.0,<1.35.0)", "mypy-boto3-globalaccelerator (>=1.34.0,<1.35.0)", "mypy-boto3-glue (>=1.34.0,<1.35.0)", "mypy-boto3-grafana (>=1.34.0,<1.35.0)", "mypy-boto3-greengrass (>=1.34.0,<1.35.0)", "mypy-boto3-greengrassv2 (>=1.34.0,<1.35.0)", "mypy-boto3-groundstation (>=1.34.0,<1.35.0)", "mypy-boto3-guardduty (>=1.34.0,<1.35.0)", "mypy-boto3-health (>=1.34.0,<1.35.0)", "mypy-boto3-healthlake (>=1.34.0,<1.35.0)", "mypy-boto3-honeycode (>=1.34.0,<1.35.0)", "mypy-boto3-iam (>=1.34.0,<1.35.0)", "mypy-boto3-identitystore (>=1.34.0,<1.35.0)", "mypy-boto3-imagebuilder (>=1.34.0,<1.35.0)", "mypy-boto3-importexport (>=1.34.0,<1.35.0)", "mypy-boto3-inspector (>=1.34.0,<1.35.0)", "mypy-boto3-inspector-scan (>=1.34.0,<1.35.0)", "mypy-boto3-inspector2 (>=1.34.0,<1.35.0)", "mypy-boto3-internetmonitor (>=1.34.0,<1.35.0)", "mypy-boto3-iot (>=1.34.0,<1.35.0)", "mypy-boto3-iot-data (>=1.34.0,<1.35.0)", "mypy-boto3-iot-jobs-data (>=1.34.0,<1.35.0)", "mypy-boto3-iot-roborunner (>=1.34.0,<1.35.0)", "mypy-boto3-iot1click-devices (>=1.34.0,<1.35.0)", "mypy-boto3-iot1click-projects (>=1.34.0,<1.35.0)", "mypy-boto3-iotanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-iotdeviceadvisor (>=1.34.0,<1.35.0)", "mypy-boto3-iotevents (>=1.34.0,<1.35.0)", "mypy-boto3-iotevents-data (>=1.34.0,<1.35.0)", "mypy-boto3-iotfleethub (>=1.34.0,<1.35.0)", "mypy-boto3-iotfleetwise (>=1.34.0,<1.35.0)", "mypy-boto3-iotsecuretunneling (>=1.34.0,<1.35.0)", "mypy-boto3-iotsitewise (>=1.34.0,<1.35.0)", "mypy-boto3-iotthingsgraph (>=1.34.0,<1.35.0)", "mypy-boto3-iottwinmaker (>=1.34.0,<1.35.0)", "mypy-boto3-iotwireless (>=1.34.0,<1.35.0)", "mypy-boto3-ivs (>=1.34.0,<1.35.0)", "mypy-boto3-ivs-realtime (>=1.34.0,<1.35.0)", "mypy-boto3-ivschat (>=1.34.0,<1.35.0)", "mypy-boto3-kafka (>=1.34.0,<1.35.0)", "mypy-boto3-kafkaconnect (>=1.34.0,<1.35.0)", "mypy-boto3-kendra (>=1.34.0,<1.35.0)", "mypy-boto3-kendra-ranking (>=1.34.0,<1.35.0)", "mypy-boto3-keyspaces (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-archived-media (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-media (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-signaling (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisvideo (>=1.34.0,<1.35.0)", "mypy-boto3-kms (>=1.34.0,<1.35.0)", "mypy-boto3-lakeformation (>=1.34.0,<1.35.0)", "mypy-boto3-lambda (>=1.34.0,<1.35.0)", "mypy-boto3-launch-wizard (>=1.34.0,<1.35.0)", "mypy-boto3-lex-models (>=1.34.0,<1.35.0)", "mypy-boto3-lex-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-lexv2-models (>=1.34.0,<1.35.0)", "mypy-boto3-lexv2-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.34.0,<1.35.0)", "mypy-boto3-lightsail (>=1.34.0,<1.35.0)", "mypy-boto3-location (>=1.34.0,<1.35.0)", "mypy-boto3-logs (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutequipment (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutmetrics (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutvision (>=1.34.0,<1.35.0)", "mypy-boto3-m2 (>=1.34.0,<1.35.0)", "mypy-boto3-machinelearning (>=1.34.0,<1.35.0)", "mypy-boto3-macie2 (>=1.34.0,<1.35.0)", "mypy-boto3-managedblockchain (>=1.34.0,<1.35.0)", "mypy-boto3-managedblockchain-query (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-agreement (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-catalog (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-deployment (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-entitlement (>=1.34.0,<1.35.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-mediaconnect (>=1.34.0,<1.35.0)", "mypy-boto3-mediaconvert (>=1.34.0,<1.35.0)", "mypy-boto3-medialive (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackage (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackage-vod (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackagev2 (>=1.34.0,<1.35.0)", "mypy-boto3-mediastore (>=1.34.0,<1.35.0)", "mypy-boto3-mediastore-data (>=1.34.0,<1.35.0)", "mypy-boto3-mediatailor (>=1.34.0,<1.35.0)", "mypy-boto3-medical-imaging (>=1.34.0,<1.35.0)", "mypy-boto3-memorydb (>=1.34.0,<1.35.0)", "mypy-boto3-meteringmarketplace (>=1.34.0,<1.35.0)", "mypy-boto3-mgh (>=1.34.0,<1.35.0)", "mypy-boto3-mgn (>=1.34.0,<1.35.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhub-config (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhuborchestrator (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhubstrategy (>=1.34.0,<1.35.0)", "mypy-boto3-mobile (>=1.34.0,<1.35.0)", "mypy-boto3-mq (>=1.34.0,<1.35.0)", "mypy-boto3-mturk (>=1.34.0,<1.35.0)", "mypy-boto3-mwaa (>=1.34.0,<1.35.0)", "mypy-boto3-neptune (>=1.34.0,<1.35.0)", "mypy-boto3-neptune-graph (>=1.34.0,<1.35.0)", "mypy-boto3-neptunedata (>=1.34.0,<1.35.0)", "mypy-boto3-network-firewall (>=1.34.0,<1.35.0)", "mypy-boto3-networkmanager (>=1.34.0,<1.35.0)", "mypy-boto3-networkmonitor (>=1.34.0,<1.35.0)", "mypy-boto3-nimble (>=1.34.0,<1.35.0)", "mypy-boto3-oam (>=1.34.0,<1.35.0)", "mypy-boto3-omics (>=1.34.0,<1.35.0)", "mypy-boto3-opensearch (>=1.34.0,<1.35.0)", "mypy-boto3-opensearchserverless (>=1.34.0,<1.35.0)", "mypy-boto3-opsworks (>=1.34.0,<1.35.0)", "mypy-boto3-opsworkscm (>=1.34.0,<1.35.0)", "mypy-boto3-organizations (>=1.34.0,<1.35.0)", "mypy-boto3-osis (>=1.34.0,<1.35.0)", "mypy-boto3-outposts (>=1.34.0,<1.35.0)", "mypy-boto3-panorama (>=1.34.0,<1.35.0)", "mypy-boto3-payment-cryptography (>=1.34.0,<1.35.0)", "mypy-boto3-payment-cryptography-data (>=1.34.0,<1.35.0)", "mypy-boto3-pca-connector-ad (>=1.34.0,<1.35.0)", "mypy-boto3-personalize (>=1.34.0,<1.35.0)", "mypy-boto3-personalize-events (>=1.34.0,<1.35.0)", "mypy-boto3-personalize-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-pi (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-email (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-sms-voice (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.34.0,<1.35.0)", "mypy-boto3-pipes (>=1.34.0,<1.35.0)", "mypy-boto3-polly (>=1.34.0,<1.35.0)", "mypy-boto3-pricing (>=1.34.0,<1.35.0)", "mypy-boto3-privatenetworks (>=1.34.0,<1.35.0)", "mypy-boto3-proton (>=1.34.0,<1.35.0)", "mypy-boto3-qbusiness (>=1.34.0,<1.35.0)", "mypy-boto3-qconnect (>=1.34.0,<1.35.0)", "mypy-boto3-qldb (>=1.34.0,<1.35.0)", "mypy-boto3-qldb-session (>=1.34.0,<1.35.0)", "mypy-boto3-quicksight (>=1.34.0,<1.35.0)", "mypy-boto3-ram (>=1.34.0,<1.35.0)", "mypy-boto3-rbin (>=1.34.0,<1.35.0)", "mypy-boto3-rds (>=1.34.0,<1.35.0)", "mypy-boto3-rds-data (>=1.34.0,<1.35.0)", "mypy-boto3-redshift (>=1.34.0,<1.35.0)", "mypy-boto3-redshift-data (>=1.34.0,<1.35.0)", "mypy-boto3-redshift-serverless (>=1.34.0,<1.35.0)", "mypy-boto3-rekognition (>=1.34.0,<1.35.0)", "mypy-boto3-repostspace (>=1.34.0,<1.35.0)", "mypy-boto3-resiliencehub (>=1.34.0,<1.35.0)", "mypy-boto3-resource-explorer-2 (>=1.34.0,<1.35.0)", "mypy-boto3-resource-groups (>=1.34.0,<1.35.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.34.0,<1.35.0)", "mypy-boto3-robomaker (>=1.34.0,<1.35.0)", "mypy-boto3-rolesanywhere (>=1.34.0,<1.35.0)", "mypy-boto3-route53 (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-cluster (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-control-config (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-readiness (>=1.34.0,<1.35.0)", "mypy-boto3-route53domains (>=1.34.0,<1.35.0)", "mypy-boto3-route53resolver (>=1.34.0,<1.35.0)", "mypy-boto3-rum (>=1.34.0,<1.35.0)", "mypy-boto3-s3 (>=1.34.0,<1.35.0)", "mypy-boto3-s3control (>=1.34.0,<1.35.0)", "mypy-boto3-s3outposts (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-edge (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-geospatial (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-metrics (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-savingsplans (>=1.34.0,<1.35.0)", "mypy-boto3-scheduler (>=1.34.0,<1.35.0)", "mypy-boto3-schemas (>=1.34.0,<1.35.0)", "mypy-boto3-sdb (>=1.34.0,<1.35.0)", "mypy-boto3-secretsmanager (>=1.34.0,<1.35.0)", "mypy-boto3-securityhub (>=1.34.0,<1.35.0)", "mypy-boto3-securitylake (>=1.34.0,<1.35.0)", "mypy-boto3-serverlessrepo (>=1.34.0,<1.35.0)", "mypy-boto3-service-quotas (>=1.34.0,<1.35.0)", "mypy-boto3-servicecatalog (>=1.34.0,<1.35.0)", "mypy-boto3-servicecatalog-appregistry (>=1.34.0,<1.35.0)", "mypy-boto3-servicediscovery (>=1.34.0,<1.35.0)", "mypy-boto3-ses (>=1.34.0,<1.35.0)", "mypy-boto3-sesv2 (>=1.34.0,<1.35.0)", "mypy-boto3-shield (>=1.34.0,<1.35.0)", "mypy-boto3-signer (>=1.34.0,<1.35.0)", "mypy-boto3-simspaceweaver (>=1.34.0,<1.35.0)", "mypy-boto3-sms (>=1.34.0,<1.35.0)", "mypy-boto3-sms-voice (>=1.34.0,<1.35.0)", "mypy-boto3-snow-device-management (>=1.34.0,<1.35.0)", "mypy-boto3-snowball (>=1.34.0,<1.35.0)", "mypy-boto3-sns (>=1.34.0,<1.35.0)", "mypy-boto3-sqs (>=1.34.0,<1.35.0)", "mypy-boto3-ssm (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-contacts (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-incidents (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-sap (>=1.34.0,<1.35.0)", "mypy-boto3-sso (>=1.34.0,<1.35.0)", "mypy-boto3-sso-admin (>=1.34.0,<1.35.0)", "mypy-boto3-sso-oidc (>=1.34.0,<1.35.0)", "mypy-boto3-stepfunctions (>=1.34.0,<1.35.0)", "mypy-boto3-storagegateway (>=1.34.0,<1.35.0)", "mypy-boto3-sts (>=1.34.0,<1.35.0)", "mypy-boto3-supplychain (>=1.34.0,<1.35.0)", "mypy-boto3-support (>=1.34.0,<1.35.0)", "mypy-boto3-support-app (>=1.34.0,<1.35.0)", "mypy-boto3-swf (>=1.34.0,<1.35.0)", "mypy-boto3-synthetics (>=1.34.0,<1.35.0)", "mypy-boto3-textract (>=1.34.0,<1.35.0)", "mypy-boto3-timestream-query (>=1.34.0,<1.35.0)", "mypy-boto3-timestream-write (>=1.34.0,<1.35.0)", "mypy-boto3-tnb (>=1.34.0,<1.35.0)", "mypy-boto3-transcribe (>=1.34.0,<1.35.0)", "mypy-boto3-transfer (>=1.34.0,<1.35.0)", "mypy-boto3-translate (>=1.34.0,<1.35.0)", "mypy-boto3-trustedadvisor (>=1.34.0,<1.35.0)", "mypy-boto3-verifiedpermissions (>=1.34.0,<1.35.0)", "mypy-boto3-voice-id (>=1.34.0,<1.35.0)", "mypy-boto3-vpc-lattice (>=1.34.0,<1.35.0)", "mypy-boto3-waf (>=1.34.0,<1.35.0)", "mypy-boto3-waf-regional (>=1.34.0,<1.35.0)", "mypy-boto3-wafv2 (>=1.34.0,<1.35.0)", "mypy-boto3-wellarchitected (>=1.34.0,<1.35.0)", "mypy-boto3-wisdom (>=1.34.0,<1.35.0)", "mypy-boto3-workdocs (>=1.34.0,<1.35.0)", "mypy-boto3-worklink (>=1.34.0,<1.35.0)", "mypy-boto3-workmail (>=1.34.0,<1.35.0)", "mypy-boto3-workmailmessageflow (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces-thin-client (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces-web (>=1.34.0,<1.35.0)", "mypy-boto3-xray (>=1.34.0,<1.35.0)"] -amp = ["mypy-boto3-amp (>=1.34.0,<1.35.0)"] -amplify = ["mypy-boto3-amplify (>=1.34.0,<1.35.0)"] -amplifybackend = ["mypy-boto3-amplifybackend (>=1.34.0,<1.35.0)"] -amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.34.0,<1.35.0)"] -apigateway = ["mypy-boto3-apigateway (>=1.34.0,<1.35.0)"] -apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.34.0,<1.35.0)"] -apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.34.0,<1.35.0)"] -appconfig = ["mypy-boto3-appconfig (>=1.34.0,<1.35.0)"] -appconfigdata = ["mypy-boto3-appconfigdata (>=1.34.0,<1.35.0)"] -appfabric = ["mypy-boto3-appfabric (>=1.34.0,<1.35.0)"] -appflow = ["mypy-boto3-appflow (>=1.34.0,<1.35.0)"] -appintegrations = ["mypy-boto3-appintegrations (>=1.34.0,<1.35.0)"] -application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.34.0,<1.35.0)"] -application-insights = ["mypy-boto3-application-insights (>=1.34.0,<1.35.0)"] -applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.34.0,<1.35.0)"] -appmesh = ["mypy-boto3-appmesh (>=1.34.0,<1.35.0)"] -apprunner = ["mypy-boto3-apprunner (>=1.34.0,<1.35.0)"] -appstream = ["mypy-boto3-appstream (>=1.34.0,<1.35.0)"] -appsync = ["mypy-boto3-appsync (>=1.34.0,<1.35.0)"] -arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.34.0,<1.35.0)"] -athena = ["mypy-boto3-athena (>=1.34.0,<1.35.0)"] -auditmanager = ["mypy-boto3-auditmanager (>=1.34.0,<1.35.0)"] -autoscaling = ["mypy-boto3-autoscaling (>=1.34.0,<1.35.0)"] -autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.34.0,<1.35.0)"] -b2bi = ["mypy-boto3-b2bi (>=1.34.0,<1.35.0)"] -backup = ["mypy-boto3-backup (>=1.34.0,<1.35.0)"] -backup-gateway = ["mypy-boto3-backup-gateway (>=1.34.0,<1.35.0)"] -backupstorage = ["mypy-boto3-backupstorage (>=1.34.0,<1.35.0)"] -batch = ["mypy-boto3-batch (>=1.34.0,<1.35.0)"] -bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.34.0,<1.35.0)"] -bedrock = ["mypy-boto3-bedrock (>=1.34.0,<1.35.0)"] -bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.34.0,<1.35.0)"] -bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.34.0,<1.35.0)"] -bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.34.0,<1.35.0)"] -billingconductor = ["mypy-boto3-billingconductor (>=1.34.0,<1.35.0)"] -boto3 = ["boto3 (==1.34.20)", "botocore (==1.34.20)"] -braket = ["mypy-boto3-braket (>=1.34.0,<1.35.0)"] -budgets = ["mypy-boto3-budgets (>=1.34.0,<1.35.0)"] -ce = ["mypy-boto3-ce (>=1.34.0,<1.35.0)"] -chime = ["mypy-boto3-chime (>=1.34.0,<1.35.0)"] -chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.34.0,<1.35.0)"] -chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.34.0,<1.35.0)"] -chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.34.0,<1.35.0)"] -chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.34.0,<1.35.0)"] -chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.34.0,<1.35.0)"] -cleanrooms = ["mypy-boto3-cleanrooms (>=1.34.0,<1.35.0)"] -cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.34.0,<1.35.0)"] -cloud9 = ["mypy-boto3-cloud9 (>=1.34.0,<1.35.0)"] -cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.34.0,<1.35.0)"] -clouddirectory = ["mypy-boto3-clouddirectory (>=1.34.0,<1.35.0)"] -cloudformation = ["mypy-boto3-cloudformation (>=1.34.0,<1.35.0)"] -cloudfront = ["mypy-boto3-cloudfront (>=1.34.0,<1.35.0)"] -cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.34.0,<1.35.0)"] -cloudhsm = ["mypy-boto3-cloudhsm (>=1.34.0,<1.35.0)"] -cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.34.0,<1.35.0)"] -cloudsearch = ["mypy-boto3-cloudsearch (>=1.34.0,<1.35.0)"] -cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.34.0,<1.35.0)"] -cloudtrail = ["mypy-boto3-cloudtrail (>=1.34.0,<1.35.0)"] -cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.34.0,<1.35.0)"] -cloudwatch = ["mypy-boto3-cloudwatch (>=1.34.0,<1.35.0)"] -codeartifact = ["mypy-boto3-codeartifact (>=1.34.0,<1.35.0)"] -codebuild = ["mypy-boto3-codebuild (>=1.34.0,<1.35.0)"] -codecatalyst = ["mypy-boto3-codecatalyst (>=1.34.0,<1.35.0)"] -codecommit = ["mypy-boto3-codecommit (>=1.34.0,<1.35.0)"] -codedeploy = ["mypy-boto3-codedeploy (>=1.34.0,<1.35.0)"] -codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.34.0,<1.35.0)"] -codeguru-security = ["mypy-boto3-codeguru-security (>=1.34.0,<1.35.0)"] -codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.34.0,<1.35.0)"] -codepipeline = ["mypy-boto3-codepipeline (>=1.34.0,<1.35.0)"] -codestar = ["mypy-boto3-codestar (>=1.34.0,<1.35.0)"] -codestar-connections = ["mypy-boto3-codestar-connections (>=1.34.0,<1.35.0)"] -codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.34.0,<1.35.0)"] -cognito-identity = ["mypy-boto3-cognito-identity (>=1.34.0,<1.35.0)"] -cognito-idp = ["mypy-boto3-cognito-idp (>=1.34.0,<1.35.0)"] -cognito-sync = ["mypy-boto3-cognito-sync (>=1.34.0,<1.35.0)"] -comprehend = ["mypy-boto3-comprehend (>=1.34.0,<1.35.0)"] -comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.34.0,<1.35.0)"] -compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.34.0,<1.35.0)"] -config = ["mypy-boto3-config (>=1.34.0,<1.35.0)"] -connect = ["mypy-boto3-connect (>=1.34.0,<1.35.0)"] -connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.34.0,<1.35.0)"] -connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.34.0,<1.35.0)"] -connectcases = ["mypy-boto3-connectcases (>=1.34.0,<1.35.0)"] -connectparticipant = ["mypy-boto3-connectparticipant (>=1.34.0,<1.35.0)"] -controltower = ["mypy-boto3-controltower (>=1.34.0,<1.35.0)"] -cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.34.0,<1.35.0)"] -cur = ["mypy-boto3-cur (>=1.34.0,<1.35.0)"] -customer-profiles = ["mypy-boto3-customer-profiles (>=1.34.0,<1.35.0)"] -databrew = ["mypy-boto3-databrew (>=1.34.0,<1.35.0)"] -dataexchange = ["mypy-boto3-dataexchange (>=1.34.0,<1.35.0)"] -datapipeline = ["mypy-boto3-datapipeline (>=1.34.0,<1.35.0)"] -datasync = ["mypy-boto3-datasync (>=1.34.0,<1.35.0)"] -datazone = ["mypy-boto3-datazone (>=1.34.0,<1.35.0)"] -dax = ["mypy-boto3-dax (>=1.34.0,<1.35.0)"] -detective = ["mypy-boto3-detective (>=1.34.0,<1.35.0)"] -devicefarm = ["mypy-boto3-devicefarm (>=1.34.0,<1.35.0)"] -devops-guru = ["mypy-boto3-devops-guru (>=1.34.0,<1.35.0)"] -directconnect = ["mypy-boto3-directconnect (>=1.34.0,<1.35.0)"] -discovery = ["mypy-boto3-discovery (>=1.34.0,<1.35.0)"] -dlm = ["mypy-boto3-dlm (>=1.34.0,<1.35.0)"] -dms = ["mypy-boto3-dms (>=1.34.0,<1.35.0)"] -docdb = ["mypy-boto3-docdb (>=1.34.0,<1.35.0)"] -docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.34.0,<1.35.0)"] -drs = ["mypy-boto3-drs (>=1.34.0,<1.35.0)"] -ds = ["mypy-boto3-ds (>=1.34.0,<1.35.0)"] -dynamodb = ["mypy-boto3-dynamodb (>=1.34.0,<1.35.0)"] -dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.34.0,<1.35.0)"] -ebs = ["mypy-boto3-ebs (>=1.34.0,<1.35.0)"] -ec2 = ["mypy-boto3-ec2 (>=1.34.0,<1.35.0)"] -ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.34.0,<1.35.0)"] -ecr = ["mypy-boto3-ecr (>=1.34.0,<1.35.0)"] -ecr-public = ["mypy-boto3-ecr-public (>=1.34.0,<1.35.0)"] -ecs = ["mypy-boto3-ecs (>=1.34.0,<1.35.0)"] -efs = ["mypy-boto3-efs (>=1.34.0,<1.35.0)"] -eks = ["mypy-boto3-eks (>=1.34.0,<1.35.0)"] -eks-auth = ["mypy-boto3-eks-auth (>=1.34.0,<1.35.0)"] -elastic-inference = ["mypy-boto3-elastic-inference (>=1.34.0,<1.35.0)"] -elasticache = ["mypy-boto3-elasticache (>=1.34.0,<1.35.0)"] -elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.34.0,<1.35.0)"] -elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.34.0,<1.35.0)"] -elb = ["mypy-boto3-elb (>=1.34.0,<1.35.0)"] -elbv2 = ["mypy-boto3-elbv2 (>=1.34.0,<1.35.0)"] -emr = ["mypy-boto3-emr (>=1.34.0,<1.35.0)"] -emr-containers = ["mypy-boto3-emr-containers (>=1.34.0,<1.35.0)"] -emr-serverless = ["mypy-boto3-emr-serverless (>=1.34.0,<1.35.0)"] -entityresolution = ["mypy-boto3-entityresolution (>=1.34.0,<1.35.0)"] -es = ["mypy-boto3-es (>=1.34.0,<1.35.0)"] -essential = ["mypy-boto3-cloudformation (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodb (>=1.34.0,<1.35.0)", "mypy-boto3-ec2 (>=1.34.0,<1.35.0)", "mypy-boto3-lambda (>=1.34.0,<1.35.0)", "mypy-boto3-rds (>=1.34.0,<1.35.0)", "mypy-boto3-s3 (>=1.34.0,<1.35.0)", "mypy-boto3-sqs (>=1.34.0,<1.35.0)"] -events = ["mypy-boto3-events (>=1.34.0,<1.35.0)"] -evidently = ["mypy-boto3-evidently (>=1.34.0,<1.35.0)"] -finspace = ["mypy-boto3-finspace (>=1.34.0,<1.35.0)"] -finspace-data = ["mypy-boto3-finspace-data (>=1.34.0,<1.35.0)"] -firehose = ["mypy-boto3-firehose (>=1.34.0,<1.35.0)"] -fis = ["mypy-boto3-fis (>=1.34.0,<1.35.0)"] -fms = ["mypy-boto3-fms (>=1.34.0,<1.35.0)"] -forecast = ["mypy-boto3-forecast (>=1.34.0,<1.35.0)"] -forecastquery = ["mypy-boto3-forecastquery (>=1.34.0,<1.35.0)"] -frauddetector = ["mypy-boto3-frauddetector (>=1.34.0,<1.35.0)"] -freetier = ["mypy-boto3-freetier (>=1.34.0,<1.35.0)"] -fsx = ["mypy-boto3-fsx (>=1.34.0,<1.35.0)"] -gamelift = ["mypy-boto3-gamelift (>=1.34.0,<1.35.0)"] -glacier = ["mypy-boto3-glacier (>=1.34.0,<1.35.0)"] -globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.34.0,<1.35.0)"] -glue = ["mypy-boto3-glue (>=1.34.0,<1.35.0)"] -grafana = ["mypy-boto3-grafana (>=1.34.0,<1.35.0)"] -greengrass = ["mypy-boto3-greengrass (>=1.34.0,<1.35.0)"] -greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.34.0,<1.35.0)"] -groundstation = ["mypy-boto3-groundstation (>=1.34.0,<1.35.0)"] -guardduty = ["mypy-boto3-guardduty (>=1.34.0,<1.35.0)"] -health = ["mypy-boto3-health (>=1.34.0,<1.35.0)"] -healthlake = ["mypy-boto3-healthlake (>=1.34.0,<1.35.0)"] -honeycode = ["mypy-boto3-honeycode (>=1.34.0,<1.35.0)"] -iam = ["mypy-boto3-iam (>=1.34.0,<1.35.0)"] -identitystore = ["mypy-boto3-identitystore (>=1.34.0,<1.35.0)"] -imagebuilder = ["mypy-boto3-imagebuilder (>=1.34.0,<1.35.0)"] -importexport = ["mypy-boto3-importexport (>=1.34.0,<1.35.0)"] -inspector = ["mypy-boto3-inspector (>=1.34.0,<1.35.0)"] -inspector-scan = ["mypy-boto3-inspector-scan (>=1.34.0,<1.35.0)"] -inspector2 = ["mypy-boto3-inspector2 (>=1.34.0,<1.35.0)"] -internetmonitor = ["mypy-boto3-internetmonitor (>=1.34.0,<1.35.0)"] -iot = ["mypy-boto3-iot (>=1.34.0,<1.35.0)"] -iot-data = ["mypy-boto3-iot-data (>=1.34.0,<1.35.0)"] -iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.34.0,<1.35.0)"] -iot-roborunner = ["mypy-boto3-iot-roborunner (>=1.34.0,<1.35.0)"] -iot1click-devices = ["mypy-boto3-iot1click-devices (>=1.34.0,<1.35.0)"] -iot1click-projects = ["mypy-boto3-iot1click-projects (>=1.34.0,<1.35.0)"] -iotanalytics = ["mypy-boto3-iotanalytics (>=1.34.0,<1.35.0)"] -iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.34.0,<1.35.0)"] -iotevents = ["mypy-boto3-iotevents (>=1.34.0,<1.35.0)"] -iotevents-data = ["mypy-boto3-iotevents-data (>=1.34.0,<1.35.0)"] -iotfleethub = ["mypy-boto3-iotfleethub (>=1.34.0,<1.35.0)"] -iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.34.0,<1.35.0)"] -iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.34.0,<1.35.0)"] -iotsitewise = ["mypy-boto3-iotsitewise (>=1.34.0,<1.35.0)"] -iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.34.0,<1.35.0)"] -iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.34.0,<1.35.0)"] -iotwireless = ["mypy-boto3-iotwireless (>=1.34.0,<1.35.0)"] -ivs = ["mypy-boto3-ivs (>=1.34.0,<1.35.0)"] -ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.34.0,<1.35.0)"] -ivschat = ["mypy-boto3-ivschat (>=1.34.0,<1.35.0)"] -kafka = ["mypy-boto3-kafka (>=1.34.0,<1.35.0)"] -kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.34.0,<1.35.0)"] -kendra = ["mypy-boto3-kendra (>=1.34.0,<1.35.0)"] -kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.34.0,<1.35.0)"] -keyspaces = ["mypy-boto3-keyspaces (>=1.34.0,<1.35.0)"] -kinesis = ["mypy-boto3-kinesis (>=1.34.0,<1.35.0)"] -kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.34.0,<1.35.0)"] -kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.34.0,<1.35.0)"] -kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.34.0,<1.35.0)"] -kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.34.0,<1.35.0)"] -kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.34.0,<1.35.0)"] -kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.34.0,<1.35.0)"] -kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.34.0,<1.35.0)"] -kms = ["mypy-boto3-kms (>=1.34.0,<1.35.0)"] -lakeformation = ["mypy-boto3-lakeformation (>=1.34.0,<1.35.0)"] -lambda = ["mypy-boto3-lambda (>=1.34.0,<1.35.0)"] -launch-wizard = ["mypy-boto3-launch-wizard (>=1.34.0,<1.35.0)"] -lex-models = ["mypy-boto3-lex-models (>=1.34.0,<1.35.0)"] -lex-runtime = ["mypy-boto3-lex-runtime (>=1.34.0,<1.35.0)"] -lexv2-models = ["mypy-boto3-lexv2-models (>=1.34.0,<1.35.0)"] -lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.34.0,<1.35.0)"] -license-manager = ["mypy-boto3-license-manager (>=1.34.0,<1.35.0)"] -license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.34.0,<1.35.0)"] -license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.34.0,<1.35.0)"] -lightsail = ["mypy-boto3-lightsail (>=1.34.0,<1.35.0)"] -location = ["mypy-boto3-location (>=1.34.0,<1.35.0)"] -logs = ["mypy-boto3-logs (>=1.34.0,<1.35.0)"] -lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.34.0,<1.35.0)"] -lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.34.0,<1.35.0)"] -lookoutvision = ["mypy-boto3-lookoutvision (>=1.34.0,<1.35.0)"] -m2 = ["mypy-boto3-m2 (>=1.34.0,<1.35.0)"] -machinelearning = ["mypy-boto3-machinelearning (>=1.34.0,<1.35.0)"] -macie2 = ["mypy-boto3-macie2 (>=1.34.0,<1.35.0)"] -managedblockchain = ["mypy-boto3-managedblockchain (>=1.34.0,<1.35.0)"] -managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.34.0,<1.35.0)"] -marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.34.0,<1.35.0)"] -marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.34.0,<1.35.0)"] -marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.34.0,<1.35.0)"] -marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.34.0,<1.35.0)"] -marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.34.0,<1.35.0)"] -mediaconnect = ["mypy-boto3-mediaconnect (>=1.34.0,<1.35.0)"] -mediaconvert = ["mypy-boto3-mediaconvert (>=1.34.0,<1.35.0)"] -medialive = ["mypy-boto3-medialive (>=1.34.0,<1.35.0)"] -mediapackage = ["mypy-boto3-mediapackage (>=1.34.0,<1.35.0)"] -mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.34.0,<1.35.0)"] -mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.34.0,<1.35.0)"] -mediastore = ["mypy-boto3-mediastore (>=1.34.0,<1.35.0)"] -mediastore-data = ["mypy-boto3-mediastore-data (>=1.34.0,<1.35.0)"] -mediatailor = ["mypy-boto3-mediatailor (>=1.34.0,<1.35.0)"] -medical-imaging = ["mypy-boto3-medical-imaging (>=1.34.0,<1.35.0)"] -memorydb = ["mypy-boto3-memorydb (>=1.34.0,<1.35.0)"] -meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.34.0,<1.35.0)"] -mgh = ["mypy-boto3-mgh (>=1.34.0,<1.35.0)"] -mgn = ["mypy-boto3-mgn (>=1.34.0,<1.35.0)"] -migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.34.0,<1.35.0)"] -migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.34.0,<1.35.0)"] -migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.34.0,<1.35.0)"] -migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.34.0,<1.35.0)"] -mobile = ["mypy-boto3-mobile (>=1.34.0,<1.35.0)"] -mq = ["mypy-boto3-mq (>=1.34.0,<1.35.0)"] -mturk = ["mypy-boto3-mturk (>=1.34.0,<1.35.0)"] -mwaa = ["mypy-boto3-mwaa (>=1.34.0,<1.35.0)"] -neptune = ["mypy-boto3-neptune (>=1.34.0,<1.35.0)"] -neptune-graph = ["mypy-boto3-neptune-graph (>=1.34.0,<1.35.0)"] -neptunedata = ["mypy-boto3-neptunedata (>=1.34.0,<1.35.0)"] -network-firewall = ["mypy-boto3-network-firewall (>=1.34.0,<1.35.0)"] -networkmanager = ["mypy-boto3-networkmanager (>=1.34.0,<1.35.0)"] -networkmonitor = ["mypy-boto3-networkmonitor (>=1.34.0,<1.35.0)"] -nimble = ["mypy-boto3-nimble (>=1.34.0,<1.35.0)"] -oam = ["mypy-boto3-oam (>=1.34.0,<1.35.0)"] -omics = ["mypy-boto3-omics (>=1.34.0,<1.35.0)"] -opensearch = ["mypy-boto3-opensearch (>=1.34.0,<1.35.0)"] -opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.34.0,<1.35.0)"] -opsworks = ["mypy-boto3-opsworks (>=1.34.0,<1.35.0)"] -opsworkscm = ["mypy-boto3-opsworkscm (>=1.34.0,<1.35.0)"] -organizations = ["mypy-boto3-organizations (>=1.34.0,<1.35.0)"] -osis = ["mypy-boto3-osis (>=1.34.0,<1.35.0)"] -outposts = ["mypy-boto3-outposts (>=1.34.0,<1.35.0)"] -panorama = ["mypy-boto3-panorama (>=1.34.0,<1.35.0)"] -payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.34.0,<1.35.0)"] -payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.34.0,<1.35.0)"] -pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.34.0,<1.35.0)"] -personalize = ["mypy-boto3-personalize (>=1.34.0,<1.35.0)"] -personalize-events = ["mypy-boto3-personalize-events (>=1.34.0,<1.35.0)"] -personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.34.0,<1.35.0)"] -pi = ["mypy-boto3-pi (>=1.34.0,<1.35.0)"] -pinpoint = ["mypy-boto3-pinpoint (>=1.34.0,<1.35.0)"] -pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.34.0,<1.35.0)"] -pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.34.0,<1.35.0)"] -pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.34.0,<1.35.0)"] -pipes = ["mypy-boto3-pipes (>=1.34.0,<1.35.0)"] -polly = ["mypy-boto3-polly (>=1.34.0,<1.35.0)"] -pricing = ["mypy-boto3-pricing (>=1.34.0,<1.35.0)"] -privatenetworks = ["mypy-boto3-privatenetworks (>=1.34.0,<1.35.0)"] -proton = ["mypy-boto3-proton (>=1.34.0,<1.35.0)"] -qbusiness = ["mypy-boto3-qbusiness (>=1.34.0,<1.35.0)"] -qconnect = ["mypy-boto3-qconnect (>=1.34.0,<1.35.0)"] -qldb = ["mypy-boto3-qldb (>=1.34.0,<1.35.0)"] -qldb-session = ["mypy-boto3-qldb-session (>=1.34.0,<1.35.0)"] -quicksight = ["mypy-boto3-quicksight (>=1.34.0,<1.35.0)"] -ram = ["mypy-boto3-ram (>=1.34.0,<1.35.0)"] -rbin = ["mypy-boto3-rbin (>=1.34.0,<1.35.0)"] -rds = ["mypy-boto3-rds (>=1.34.0,<1.35.0)"] -rds-data = ["mypy-boto3-rds-data (>=1.34.0,<1.35.0)"] -redshift = ["mypy-boto3-redshift (>=1.34.0,<1.35.0)"] -redshift-data = ["mypy-boto3-redshift-data (>=1.34.0,<1.35.0)"] -redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.34.0,<1.35.0)"] -rekognition = ["mypy-boto3-rekognition (>=1.34.0,<1.35.0)"] -repostspace = ["mypy-boto3-repostspace (>=1.34.0,<1.35.0)"] -resiliencehub = ["mypy-boto3-resiliencehub (>=1.34.0,<1.35.0)"] -resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.34.0,<1.35.0)"] -resource-groups = ["mypy-boto3-resource-groups (>=1.34.0,<1.35.0)"] -resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.34.0,<1.35.0)"] -robomaker = ["mypy-boto3-robomaker (>=1.34.0,<1.35.0)"] -rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.34.0,<1.35.0)"] -route53 = ["mypy-boto3-route53 (>=1.34.0,<1.35.0)"] -route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.34.0,<1.35.0)"] -route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.34.0,<1.35.0)"] -route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.34.0,<1.35.0)"] -route53domains = ["mypy-boto3-route53domains (>=1.34.0,<1.35.0)"] -route53resolver = ["mypy-boto3-route53resolver (>=1.34.0,<1.35.0)"] -rum = ["mypy-boto3-rum (>=1.34.0,<1.35.0)"] -s3 = ["mypy-boto3-s3 (>=1.34.0,<1.35.0)"] -s3control = ["mypy-boto3-s3control (>=1.34.0,<1.35.0)"] -s3outposts = ["mypy-boto3-s3outposts (>=1.34.0,<1.35.0)"] -sagemaker = ["mypy-boto3-sagemaker (>=1.34.0,<1.35.0)"] -sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.34.0,<1.35.0)"] -sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.34.0,<1.35.0)"] -sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.34.0,<1.35.0)"] -sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.34.0,<1.35.0)"] -sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.34.0,<1.35.0)"] -sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.34.0,<1.35.0)"] -savingsplans = ["mypy-boto3-savingsplans (>=1.34.0,<1.35.0)"] -scheduler = ["mypy-boto3-scheduler (>=1.34.0,<1.35.0)"] -schemas = ["mypy-boto3-schemas (>=1.34.0,<1.35.0)"] -sdb = ["mypy-boto3-sdb (>=1.34.0,<1.35.0)"] -secretsmanager = ["mypy-boto3-secretsmanager (>=1.34.0,<1.35.0)"] -securityhub = ["mypy-boto3-securityhub (>=1.34.0,<1.35.0)"] -securitylake = ["mypy-boto3-securitylake (>=1.34.0,<1.35.0)"] -serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.34.0,<1.35.0)"] -service-quotas = ["mypy-boto3-service-quotas (>=1.34.0,<1.35.0)"] -servicecatalog = ["mypy-boto3-servicecatalog (>=1.34.0,<1.35.0)"] -servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.34.0,<1.35.0)"] -servicediscovery = ["mypy-boto3-servicediscovery (>=1.34.0,<1.35.0)"] -ses = ["mypy-boto3-ses (>=1.34.0,<1.35.0)"] -sesv2 = ["mypy-boto3-sesv2 (>=1.34.0,<1.35.0)"] -shield = ["mypy-boto3-shield (>=1.34.0,<1.35.0)"] -signer = ["mypy-boto3-signer (>=1.34.0,<1.35.0)"] -simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.34.0,<1.35.0)"] -sms = ["mypy-boto3-sms (>=1.34.0,<1.35.0)"] -sms-voice = ["mypy-boto3-sms-voice (>=1.34.0,<1.35.0)"] -snow-device-management = ["mypy-boto3-snow-device-management (>=1.34.0,<1.35.0)"] -snowball = ["mypy-boto3-snowball (>=1.34.0,<1.35.0)"] -sns = ["mypy-boto3-sns (>=1.34.0,<1.35.0)"] -sqs = ["mypy-boto3-sqs (>=1.34.0,<1.35.0)"] -ssm = ["mypy-boto3-ssm (>=1.34.0,<1.35.0)"] -ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.34.0,<1.35.0)"] -ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.34.0,<1.35.0)"] -ssm-sap = ["mypy-boto3-ssm-sap (>=1.34.0,<1.35.0)"] -sso = ["mypy-boto3-sso (>=1.34.0,<1.35.0)"] -sso-admin = ["mypy-boto3-sso-admin (>=1.34.0,<1.35.0)"] -sso-oidc = ["mypy-boto3-sso-oidc (>=1.34.0,<1.35.0)"] -stepfunctions = ["mypy-boto3-stepfunctions (>=1.34.0,<1.35.0)"] -storagegateway = ["mypy-boto3-storagegateway (>=1.34.0,<1.35.0)"] -sts = ["mypy-boto3-sts (>=1.34.0,<1.35.0)"] -supplychain = ["mypy-boto3-supplychain (>=1.34.0,<1.35.0)"] -support = ["mypy-boto3-support (>=1.34.0,<1.35.0)"] -support-app = ["mypy-boto3-support-app (>=1.34.0,<1.35.0)"] -swf = ["mypy-boto3-swf (>=1.34.0,<1.35.0)"] -synthetics = ["mypy-boto3-synthetics (>=1.34.0,<1.35.0)"] -textract = ["mypy-boto3-textract (>=1.34.0,<1.35.0)"] -timestream-query = ["mypy-boto3-timestream-query (>=1.34.0,<1.35.0)"] -timestream-write = ["mypy-boto3-timestream-write (>=1.34.0,<1.35.0)"] -tnb = ["mypy-boto3-tnb (>=1.34.0,<1.35.0)"] -transcribe = ["mypy-boto3-transcribe (>=1.34.0,<1.35.0)"] -transfer = ["mypy-boto3-transfer (>=1.34.0,<1.35.0)"] -translate = ["mypy-boto3-translate (>=1.34.0,<1.35.0)"] -trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.34.0,<1.35.0)"] -verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.34.0,<1.35.0)"] -voice-id = ["mypy-boto3-voice-id (>=1.34.0,<1.35.0)"] -vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.34.0,<1.35.0)"] -waf = ["mypy-boto3-waf (>=1.34.0,<1.35.0)"] -waf-regional = ["mypy-boto3-waf-regional (>=1.34.0,<1.35.0)"] -wafv2 = ["mypy-boto3-wafv2 (>=1.34.0,<1.35.0)"] -wellarchitected = ["mypy-boto3-wellarchitected (>=1.34.0,<1.35.0)"] -wisdom = ["mypy-boto3-wisdom (>=1.34.0,<1.35.0)"] -workdocs = ["mypy-boto3-workdocs (>=1.34.0,<1.35.0)"] -worklink = ["mypy-boto3-worklink (>=1.34.0,<1.35.0)"] -workmail = ["mypy-boto3-workmail (>=1.34.0,<1.35.0)"] -workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.34.0,<1.35.0)"] -workspaces = ["mypy-boto3-workspaces (>=1.34.0,<1.35.0)"] -workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.34.0,<1.35.0)"] -workspaces-web = ["mypy-boto3-workspaces-web (>=1.34.0,<1.35.0)"] -xray = ["mypy-boto3-xray (>=1.34.0,<1.35.0)"] - [[package]] name = "botocore" version = "1.34.20" @@ -959,23 +564,6 @@ urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""} [package.extras] crt = ["awscrt (==0.19.19)"] -[[package]] -name = "botocore-stubs" -version = "1.34.20" -description = "Type annotations and code completion for botocore" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "botocore_stubs-1.34.20-py3-none-any.whl", hash = "sha256:45296ac7942578eb6baca49589115e9bfe3a803bfd3160fa42426381084f41cb"}, - {file = "botocore_stubs-1.34.20.tar.gz", hash = "sha256:e85c2716858cbed5b935133fd681537b64bb991ad4fb43d55295edfffaf494e9"}, -] - -[package.dependencies] -types-awscrt = "*" - -[package.extras] -botocore = ["botocore"] - [[package]] name = "bs4" version = "0.0.1" @@ -2092,12 +1680,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -2230,8 +1818,8 @@ google-cloud-audit-log = ">=0.1.0,<1.0.0dev" google-cloud-core = ">=2.0.0,<3.0.0dev" grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = [ - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, ] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" @@ -2444,6 +2032,25 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "groq" +version = "0.8.0" +description = "The official Python library for the groq API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "groq-0.8.0-py3-none-any.whl", hash = "sha256:f5e4e892d45001241a930db451e633ca1f0007e3f749deaa5d7360062fcd61e3"}, + {file = "groq-0.8.0.tar.gz", hash = "sha256:37ceb2f706bd516d0bfcac8e89048a24b375172987a0d6bd9efb521c54f6deff"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +typing-extensions = ">=4.7,<5" + [[package]] name = "grpc-google-iam-v1" version = "0.13.0" @@ -3673,67 +3280,6 @@ files = [ {file = "murmurhash-1.0.10.tar.gz", hash = "sha256:5282aab1317804c6ebd6dd7f69f15ba9075aee671c44a34be2bde0f1b11ef88a"}, ] -[[package]] -name = "mypy" -version = "1.8.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-boto3-s3" -version = "1.34.14" -description = "Type annotations for boto3.S3 1.34.14 service generated with mypy-boto3-builder 7.21.0" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-boto3-s3-1.34.14.tar.gz", hash = "sha256:71c39ab0623cdb442d225b71c1783f6a513cff4c4a13505a2efbb2e3aff2e965"}, - {file = "mypy_boto3_s3-1.34.14-py3-none-any.whl", hash = "sha256:f9669ecd182d5bf3532f5f2dcc5e5237776afe157ad5a0b37b26d6bec5fcc432"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -4226,9 +3772,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4968,13 +4514,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.363" +version = "1.1.364" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.363-py3-none-any.whl", hash = "sha256:d3b8d73c8d230e26cc3523862f3398032a0c39a00d7bb69dc0f595f8e888fd01"}, - {file = "pyright-1.1.363.tar.gz", hash = "sha256:00a8f0ae0e339473bb0488f8a2a2dcdf574e94a16cd7b4390d49d144714d8db2"}, + {file = "pyright-1.1.364-py3-none-any.whl", hash = "sha256:865f1e02873c5dc7427c95acf53659a118574010e6fb364e27e47ec5c46a9f26"}, + {file = "pyright-1.1.364.tar.gz", hash = "sha256:612a2106a4078ec57efc22b5620729e9bdf4a3c17caba013b534bd33f7d08e5a"}, ] [package.dependencies] @@ -5273,7 +4819,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -6218,47 +5763,47 @@ torch = ["torch (>=1.6.0)"] [[package]] name = "tiktoken" -version = "0.5.2" +version = "0.7.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.8" files = [ - {file = "tiktoken-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c4e654282ef05ec1bd06ead22141a9a1687991cef2c6a81bdd1284301abc71d"}, - {file = "tiktoken-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b3134aa24319f42c27718c6967f3c1916a38a715a0fa73d33717ba121231307"}, - {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6092e6e77730929c8c6a51bb0d7cfdf1b72b63c4d033d6258d1f2ee81052e9e5"}, - {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ad8ae2a747622efae75837abba59be6c15a8f31b4ac3c6156bc56ec7a8e631"}, - {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51cba7c8711afa0b885445f0637f0fcc366740798c40b981f08c5f984e02c9d1"}, - {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3d8c7d2c9313f8e92e987d585ee2ba0f7c40a0de84f4805b093b634f792124f5"}, - {file = "tiktoken-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:692eca18c5fd8d1e0dde767f895c17686faaa102f37640e884eecb6854e7cca7"}, - {file = "tiktoken-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:138d173abbf1ec75863ad68ca289d4da30caa3245f3c8d4bfb274c4d629a2f77"}, - {file = "tiktoken-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7388fdd684690973fdc450b47dfd24d7f0cbe658f58a576169baef5ae4658607"}, - {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a114391790113bcff670c70c24e166a841f7ea8f47ee2fe0e71e08b49d0bf2d4"}, - {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca96f001e69f6859dd52926d950cfcc610480e920e576183497ab954e645e6ac"}, - {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:15fed1dd88e30dfadcdd8e53a8927f04e1f6f81ad08a5ca824858a593ab476c7"}, - {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f8e692db5756f7ea8cb0cfca34638316dcf0841fb8469de8ed7f6a015ba0b0"}, - {file = "tiktoken-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:bcae1c4c92df2ffc4fe9f475bf8148dbb0ee2404743168bbeb9dcc4b79dc1fdd"}, - {file = "tiktoken-0.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b76a1e17d4eb4357d00f0622d9a48ffbb23401dcf36f9716d9bd9c8e79d421aa"}, - {file = "tiktoken-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01d8b171bb5df4035580bc26d4f5339a6fd58d06f069091899d4a798ea279d3e"}, - {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42adf7d4fb1ed8de6e0ff2e794a6a15005f056a0d83d22d1d6755a39bffd9e7f"}, - {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3f894dbe0adb44609f3d532b8ea10820d61fdcb288b325a458dfc60fefb7db"}, - {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58ccfddb4e62f0df974e8f7e34a667981d9bb553a811256e617731bf1d007d19"}, - {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58902a8bad2de4268c2a701f1c844d22bfa3cbcc485b10e8e3e28a050179330b"}, - {file = "tiktoken-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e39257826d0647fcac403d8fa0a474b30d02ec8ffc012cfaf13083e9b5e82c5"}, - {file = "tiktoken-0.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bde3b0fbf09a23072d39c1ede0e0821f759b4fa254a5f00078909158e90ae1f"}, - {file = "tiktoken-0.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ddee082dcf1231ccf3a591d234935e6acf3e82ee28521fe99af9630bc8d2a60"}, - {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35c057a6a4e777b5966a7540481a75a31429fc1cb4c9da87b71c8b75b5143037"}, - {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c4a049b87e28f1dc60509f8eb7790bc8d11f9a70d99b9dd18dfdd81a084ffe6"}, - {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5bf5ce759089f4f6521ea6ed89d8f988f7b396e9f4afb503b945f5c949c6bec2"}, - {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c964f554af1a96884e01188f480dad3fc224c4bbcf7af75d4b74c4b74ae0125"}, - {file = "tiktoken-0.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:368dd5726d2e8788e47ea04f32e20f72a2012a8a67af5b0b003d1e059f1d30a3"}, - {file = "tiktoken-0.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2deef9115b8cd55536c0a02c0203512f8deb2447f41585e6d929a0b878a0dd2"}, - {file = "tiktoken-0.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ed7d380195affbf886e2f8b92b14edfe13f4768ff5fc8de315adba5b773815e"}, - {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76fce01309c8140ffe15eb34ded2bb94789614b7d1d09e206838fc173776a18"}, - {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60a5654d6a2e2d152637dd9a880b4482267dfc8a86ccf3ab1cec31a8c76bfae8"}, - {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41d4d3228e051b779245a8ddd21d4336f8975563e92375662f42d05a19bdff41"}, - {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c1cdec2c92fcde8c17a50814b525ae6a88e8e5b02030dc120b76e11db93f13"}, - {file = "tiktoken-0.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:84ddb36faedb448a50b246e13d1b6ee3437f60b7169b723a4b2abad75e914f3e"}, - {file = "tiktoken-0.5.2.tar.gz", hash = "sha256:f54c581f134a8ea96ce2023ab221d4d4d81ab614efa0b2fbce926387deb56c80"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5"}, + {file = "tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89"}, + {file = "tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97"}, + {file = "tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2398fecd38c921bcd68418675a6d155fad5f5e14c2e92fcf5fe566fa5485a858"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f5f6afb52fb8a7ea1c811e435e4188f2bef81b5e0f7a8635cc79b0eef0193d6"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861f9ee616766d736be4147abac500732b505bf7013cfaf019b85892637f235e"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54031f95c6939f6b78122c0aa03a93273a96365103793a22e1793ee86da31685"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fffdcb319b614cf14f04d02a52e26b1d1ae14a570f90e9b55461a72672f7b13d"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c72baaeaefa03ff9ba9688624143c858d1f6b755bb85d456d59e529e17234769"}, + {file = "tiktoken-0.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:131b8aeb043a8f112aad9f46011dced25d62629091e51d9dc1adbf4a1cc6aa98"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cabc6dc77460df44ec5b879e68692c63551ae4fae7460dd4ff17181df75f1db7"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d57f29171255f74c0aeacd0651e29aa47dff6f070cb9f35ebc14c82278f3b25"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ee92776fdbb3efa02a83f968c19d4997a55c8e9ce7be821ceee04a1d1ee149c"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e215292e99cb41fbc96988ef62ea63bb0ce1e15f2c147a61acc319f8b4cbe5bf"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a81bac94769cab437dd3ab0b8a4bc4e0f9cf6835bcaa88de71f39af1791727a"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6d73ea93e91d5ca771256dfc9d1d29f5a554b83821a1dc0891987636e0ae226"}, + {file = "tiktoken-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:2bcb28ddf79ffa424f171dfeef9a4daff61a94c631ca6813f43967cb263b83b9"}, + {file = "tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6"}, ] [package.dependencies] @@ -6497,17 +6042,6 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2 doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -[[package]] -name = "types-awscrt" -version = "0.20.0" -description = "Type annotations and code completion for awscrt" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "types_awscrt-0.20.0-py3-none-any.whl", hash = "sha256:e872b65d041687ec7fb49fb4dcb871ff10ade5efeca02722e037a03bff81db7e"}, - {file = "types_awscrt-0.20.0.tar.gz", hash = "sha256:99778c952e1eae10cc7a53468413001177026c9434345bf00120bb2ea5b79109"}, -] - [[package]] name = "types-beautifulsoup4" version = "4.12.0.20240106" @@ -6580,17 +6114,6 @@ files = [ [package.dependencies] types-urllib3 = "*" -[[package]] -name = "types-s3transfer" -version = "0.10.0" -description = "Type annotations and code completion for s3transfer" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "types_s3transfer-0.10.0-py3-none-any.whl", hash = "sha256:44fcdf0097b924a9aab1ee4baa1179081a9559ca62a88c807e2b256893ce688f"}, - {file = "types_s3transfer-0.10.0.tar.gz", hash = "sha256:35e4998c25df7f8985ad69dedc8e4860e8af3b43b7615e940d53c00d413bdc69"}, -] - [[package]] name = "types-urllib3" version = "1.26.25.14" @@ -6778,6 +6301,47 @@ files = [ [package.dependencies] colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\" and python_version >= \"3.7\""} +[[package]] +name = "watchdog" +version = "4.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, + {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, + {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, + {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, + {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, + {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, + {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, + {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + [[package]] name = "watchfiles" version = "0.21.0" @@ -7228,4 +6792,4 @@ benchmark = ["agbenchmark"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "d02fb1e1d7a54196516b47bd2e611b24ec5c27646855bb311a503cc5e1b09d67" +content-hash = "1c4d3c4cd63a46bdf9ebe1707c30adb0afc4e8d2ea886d7174bee43b956a278e" diff --git a/autogpts/autogpt/pyproject.toml b/autogpt/pyproject.toml similarity index 60% rename from autogpts/autogpt/pyproject.toml rename to autogpt/pyproject.toml index feef965d69..8aa0977789 100644 --- a/autogpts/autogpt/pyproject.toml +++ b/autogpt/pyproject.toml @@ -1,12 +1,10 @@ [tool.poetry] name = "agpt" version = "0.5.0" -authors = [ - "Significant Gravitas ", -] +authors = ["Significant Gravitas "] readme = "README.md" description = "An open-source attempt to make GPT-4 autonomous" -homepage = "https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt" +homepage = "https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", @@ -24,17 +22,16 @@ serve = "autogpt.app.cli:serve" python = "^3.10" anthropic = "^0.25.1" autogpt-forge = { path = "../forge", develop = true } -# autogpt-forge = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", subdirectory = "autogpts/forge"} +# autogpt-forge = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", subdirectory = "forge"} beautifulsoup4 = "^4.12.2" charset-normalizer = "^3.1.0" click = "*" colorama = "^0.4.6" distro = "^1.8.0" -en-core-web-sm = {url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl"} +en-core-web-sm = { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl" } fastapi = "^0.109.1" ftfy = "^6.1.1" google-api-python-client = "*" -gTTS = "^2.3.1" hypercorn = "^0.14.4" inflection = "*" jsonschema = "*" @@ -51,16 +48,13 @@ requests = "*" sentry-sdk = "^1.40.4" spacy = "^3.7.4" tenacity = "^8.2.2" -tiktoken = "^0.5.0" # OpenAI and Generic plugins import openapi-python-client = "^0.14.0" # Benchmarking -agbenchmark = { path = "../../benchmark", optional = true } +agbenchmark = { path = "../benchmark", optional = true } # agbenchmark = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", subdirectory = "benchmark", optional = true} -google-cloud-logging = "^3.8.0" -google-cloud-storage = "^2.13.0" psycopg2-binary = "^2.9.9" ruff = "^0.4.4" pyright = "^1.1.362" @@ -70,13 +64,12 @@ ptyprocess = "^0.7.0" benchmark = ["agbenchmark"] [tool.poetry.group.dev.dependencies] -black = "*" -boto3-stubs = {extras = ["s3"], version = "^1.33.6"} -flake8 = "*" +black = "^23.12.1" +flake8 = "^7.0.0" gitpython = "^3.1.32" -isort = "*" -mypy = "*" +isort = "^5.13.1" pre-commit = "*" +pyright = "^1.1.364" types-beautifulsoup4 = "*" types-colorama = "*" types-Markdown = "*" @@ -93,7 +86,7 @@ pytest-integration = "*" pytest-mock = "*" pytest-recording = "*" pytest-xdist = "*" -vcrpy = {git = "https://github.com/Significant-Gravitas/vcrpy.git", rev = "master"} +vcrpy = { git = "https://github.com/Significant-Gravitas/vcrpy.git", rev = "master" } [build-system] @@ -105,50 +98,18 @@ build-backend = "poetry.core.masonry.api" line-length = 88 target-version = ['py310'] include = '\.pyi?$' -packages = ["autogpt"] -extend-exclude = '.+/(dist|.venv|venv|build|data)/.+' [tool.isort] profile = "black" -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -ensure_newline_before_comments = true -line_length = 88 -sections = [ - "FUTURE", - "STDLIB", - "THIRDPARTY", - "FIRSTPARTY", - "LOCALFOLDER" -] -extend_skip = [ - "agbenchmark_config/temp_folder/", - "data/", -] +skip_glob = ["data"] -[tool.mypy] -follow_imports = 'skip' -check_untyped_defs = true -disallow_untyped_calls = true -files = [ - 'autogpt/**/*.py', - 'tests/**/*.py' -] - -[[tool.mypy.overrides]] -module = [ - 'requests.*', - 'yaml.*' -] -ignore_missing_imports = true +[tool.pyright] +pythonVersion = "3.10" +exclude = ["data/**", "**/node_modules", "**/__pycache__", "**/.*"] +ignore = ["../forge/**"] [tool.pytest.ini_options] -markers = [ - "requires_openai_api_key", - "requires_huggingface_api_key" -] +markers = ["slow", "requires_openai_api_key", "requires_huggingface_api_key"] diff --git a/autogpts/autogpt/run b/autogpt/run similarity index 100% rename from autogpts/autogpt/run rename to autogpt/run diff --git a/autogpts/autogpt/run_benchmark b/autogpt/run_benchmark similarity index 100% rename from autogpts/autogpt/run_benchmark rename to autogpt/run_benchmark diff --git a/autogpts/autogpt/autogpt/core/__init__.py b/autogpt/scripts/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/core/__init__.py rename to autogpt/scripts/__init__.py diff --git a/autogpts/autogpt/scripts/check_requirements.py b/autogpt/scripts/check_requirements.py similarity index 85% rename from autogpts/autogpt/scripts/check_requirements.py rename to autogpt/scripts/check_requirements.py index c62520825c..42939d3977 100644 --- a/autogpts/autogpt/scripts/check_requirements.py +++ b/autogpt/scripts/check_requirements.py @@ -4,12 +4,12 @@ import sys from importlib.metadata import version try: - import poetry.factory # noqa + import poetry.factory # type: ignore # noqa except ModuleNotFoundError: os.system(f"{sys.executable} -m pip install 'poetry>=1.6.1,<2.0.0'") -from poetry.core.constraints.version.version import Version -from poetry.factory import Factory +from poetry.core.constraints.version.version import Version # type: ignore +from poetry.factory import Factory # type: ignore def main(): diff --git a/autogpts/autogpt/scripts/git_log_to_release_notes.py b/autogpt/scripts/git_log_to_release_notes.py similarity index 93% rename from autogpts/autogpt/scripts/git_log_to_release_notes.py rename to autogpt/scripts/git_log_to_release_notes.py index ba121e406f..9d8afd6a49 100755 --- a/autogpts/autogpt/scripts/git_log_to_release_notes.py +++ b/autogpt/scripts/git_log_to_release_notes.py @@ -9,7 +9,7 @@ from forge.llm.providers import ChatMessage, MultiProvider from forge.llm.providers.anthropic import AnthropicModelName from git import Repo, TagReference -from autogpt.core.runner.client_lib.utils import coroutine +from autogpt.app.utils import coroutine @click.command() @@ -20,7 +20,7 @@ from autogpt.core.runner.client_lib.utils import coroutine ) @coroutine async def generate_release_notes(repo_path: Optional[Path] = None): - logger = logging.getLogger(generate_release_notes.name) + logger = logging.getLogger(generate_release_notes.name) # pyright: ignore repo = Repo(repo_path, search_parent_directories=True) tags = list(repo.tags) @@ -55,7 +55,7 @@ async def generate_release_notes(repo_path: Optional[Path] = None): git_log = repo.git.log( f"{last_release_tag.name}...{new_release_ref}", - "autogpts/autogpt/", + "autogpt/", no_merges=True, follow=True, ) @@ -80,7 +80,7 @@ async def generate_release_notes(repo_path: Optional[Path] = None): EXAMPLE_RELEASE_NOTES = """ First some important notes w.r.t. using the application: * `run.sh` has been renamed to `autogpt.sh` -* The project has been restructured. The AutoGPT Agent is now located in `autogpts/autogpt`. +* The project has been restructured. The AutoGPT Agent is now located in `autogpt`. * The application no longer uses a single workspace for all tasks. Instead, every task that you run the agent on creates a new workspace folder. See the [usage guide](https://docs.agpt.co/autogpt/usage/#workspace) for more information. ## New features ✨ @@ -89,12 +89,12 @@ First some important notes w.r.t. using the application: Our agent now works with the [Agent Protocol](/#-agent-protocol), a REST API that allows creating tasks and executing the agent's step-by-step process. This allows integration with other applications, and we also use it to connect to the agent through the UI. * **UI 💻** With the aforementioned Agent Protocol integration comes the benefit of using our own open-source Agent UI. Easily create, use, and chat with multiple agents from one interface. - When starting the application through the project's new [CLI](/#-cli), it runs with the new frontend by default, with benchmarking capabilities. Running `autogpt.sh serve` in the subproject folder (`autogpts/autogpt`) will also serve the new frontend, but without benchmarking functionality. + When starting the application through the project's new [CLI](/#-cli), it runs with the new frontend by default, with benchmarking capabilities. Running `autogpt.sh serve` in the subproject folder (`autogpt`) will also serve the new frontend, but without benchmarking functionality. Running the application the "old-fashioned" way, with the terminal interface (let's call it TTY mode), is still possible with `autogpt.sh run`. * **Resuming agents 🔄️** In TTY mode, the application will now save the agent's state when quitting, and allows resuming where you left off at a later time! * **GCS and S3 workspace backends 📦** - To further support running the application as part of a larger system, Google Cloud Storage and S3 workspace backends were added. Configuration options for this can be found in [`.env.template`](/autogpts/autogpt/.env.template). + To further support running the application as part of a larger system, Google Cloud Storage and S3 workspace backends were added. Configuration options for this can be found in [`.env.template`](/autogpt/.env.template). * **Documentation Rewrite 📖** The [documentation](https://docs.agpt.co) has been restructured and mostly rewritten to clarify and simplify the instructions, and also to accommodate the other subprojects that are now in the repo. * **New Project CLI 🔧** @@ -110,8 +110,8 @@ First some important notes w.r.t. using the application: This is mostly made possible by the `autogpt.core.configuration` module, which was also expanded with a few new features for it. Most notably, the new `from_env` attribute on the `UserConfigurable` field decorator and corresponding logic in `SystemConfiguration.from_env()` and related functions. * **Monorepo** As mentioned, the repo has been restructured to accommodate the AutoGPT Agent, Forge, AGBenchmark and the new Frontend. - * AutoGPT Agent has been moved to `autogpts/autogpt` - * Forge now lives in `autogpts/forge`, and the project's new CLI makes it easy to create new Forge-based agents. + * AutoGPT Agent has been moved to `autogpt` + * Forge now lives in `forge`, and the project's new CLI makes it easy to create new Forge-based agents. * AGBenchmark -> `benchmark` * Frontend -> `frontend` diff --git a/autogpts/autogpt/setup b/autogpt/setup similarity index 100% rename from autogpts/autogpt/setup rename to autogpt/setup diff --git a/autogpts/autogpt/autogpt/core/runner/cli_app/__init__.py b/autogpt/tests/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/core/runner/cli_app/__init__.py rename to autogpt/tests/__init__.py diff --git a/autogpts/autogpt/tests/conftest.py b/autogpt/tests/conftest.py similarity index 92% rename from autogpts/autogpt/tests/conftest.py rename to autogpt/tests/conftest.py index 4aae58bd71..efd9e9e34b 100644 --- a/autogpts/autogpt/tests/conftest.py +++ b/autogpt/tests/conftest.py @@ -12,7 +12,7 @@ from forge.file_storage.local import ( FileStorageConfiguration, LocalFileStorage, ) -from forge.llm.providers import ChatModelProvider +from forge.llm.providers import MultiProvider from forge.logging.config import configure_logging from autogpt.agents.agent import Agent, AgentConfiguration, AgentSettings @@ -71,14 +71,12 @@ def setup_logger(config: Config): @pytest.fixture -def llm_provider(config: Config) -> ChatModelProvider: +def llm_provider(config: Config) -> MultiProvider: return _configure_llm_provider(config) @pytest.fixture -def agent( - config: Config, llm_provider: ChatModelProvider, storage: FileStorage -) -> Agent: +def agent(config: Config, llm_provider: MultiProvider, storage: FileStorage) -> Agent: ai_profile = AIProfile( ai_name="Base", ai_role="A base AI", diff --git a/autogpts/autogpt/tests/context.py b/autogpt/tests/context.py similarity index 100% rename from autogpts/autogpt/tests/context.py rename to autogpt/tests/context.py diff --git a/autogpts/autogpt/autogpt/core/runner/cli_web_app/__init__.py b/autogpt/tests/integration/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/core/runner/cli_web_app/__init__.py rename to autogpt/tests/integration/__init__.py diff --git a/autogpts/autogpt/tests/integration/agent_factory.py b/autogpt/tests/integration/agent_factory.py similarity index 83% rename from autogpts/autogpt/tests/integration/agent_factory.py rename to autogpt/tests/integration/agent_factory.py index c518f1f919..416a240f5b 100644 --- a/autogpts/autogpt/tests/integration/agent_factory.py +++ b/autogpt/tests/integration/agent_factory.py @@ -1,13 +1,16 @@ +from pathlib import Path + import pytest from forge.config.ai_profile import AIProfile from forge.config.config import Config from forge.file_storage import FileStorageBackendName, get_storage +from forge.llm.providers import MultiProvider from autogpt.agents.agent import Agent, AgentConfiguration, AgentSettings @pytest.fixture -def dummy_agent(config: Config, llm_provider, memory_json_file): +def dummy_agent(config: Config, llm_provider: MultiProvider): ai_profile = AIProfile( ai_name="Dummy Agent", ai_role="Dummy Role", @@ -31,7 +34,9 @@ def dummy_agent(config: Config, llm_provider, memory_json_file): local = config.file_storage_backend == FileStorageBackendName.LOCAL restrict_to_root = not local or config.restrict_to_workspace file_storage = get_storage( - config.file_storage_backend, root_path="data", restrict_to_root=restrict_to_root + config.file_storage_backend, + root_path=Path("data"), + restrict_to_root=restrict_to_root, ) file_storage.initialize() diff --git a/autogpts/autogpt/tests/integration/test_execute_code.py b/autogpt/tests/integration/test_execute_code.py similarity index 96% rename from autogpts/autogpt/tests/integration/test_execute_code.py rename to autogpt/tests/integration/test_execute_code.py index 3d993e6c5a..6e8e147d5a 100644 --- a/autogpts/autogpt/tests/integration/test_execute_code.py +++ b/autogpt/tests/integration/test_execute_code.py @@ -4,7 +4,7 @@ import tempfile from pathlib import Path import pytest -from forge.components.code_executor import ( +from forge.components.code_executor.code_executor import ( ALLOWLIST_CONTROL, CodeExecutorComponent, is_docker_available, @@ -84,7 +84,8 @@ def test_execute_python_file_args( assert result == f"{random_args_string}\n" -def test_execute_python_code( +@pytest.mark.asyncio +async def test_execute_python_code( code_executor_component: CodeExecutorComponent, random_code: str, random_string: str, @@ -93,7 +94,7 @@ def test_execute_python_code( if not (is_docker_available() or we_are_running_in_a_docker_container()): pytest.skip("Docker is not available") - result: str = code_executor_component.execute_python_code(random_code) + result: str = await code_executor_component.execute_python_code(random_code) assert result.replace("\r", "") == f"Hello {random_string}!\n" diff --git a/autogpts/autogpt/tests/integration/test_image_gen.py b/autogpt/tests/integration/test_image_gen.py similarity index 93% rename from autogpts/autogpt/tests/integration/test_image_gen.py rename to autogpt/tests/integration/test_image_gen.py index e7427e22bc..0a49280399 100644 --- a/autogpts/autogpt/tests/integration/test_image_gen.py +++ b/autogpt/tests/integration/test_image_gen.py @@ -257,17 +257,3 @@ def test_huggingface_fail_request_bad_image( result = image_gen_component.generate_image("astronaut riding a horse", 512) assert result == "Error creating image." - - -def test_huggingface_fail_missing_api_token( - mocker, image_gen_component: ImageGeneratorComponent, agent: Agent -): - agent.legacy_config.image_provider = "huggingface" - agent.legacy_config.huggingface_image_model = "CompVis/stable-diffusion-v1-4" - - # Mock requests.post to raise ValueError - mocker.patch("requests.post", side_effect=ValueError) - - # Verify request raises an error. - with pytest.raises(ValueError): - image_gen_component.generate_image("astronaut riding a horse", 512) diff --git a/autogpts/autogpt/tests/integration/test_setup.py b/autogpt/tests/integration/test_setup.py similarity index 100% rename from autogpts/autogpt/tests/integration/test_setup.py rename to autogpt/tests/integration/test_setup.py diff --git a/autogpts/autogpt/tests/integration/test_web_selenium.py b/autogpt/tests/integration/test_web_selenium.py similarity index 100% rename from autogpts/autogpt/tests/integration/test_web_selenium.py rename to autogpt/tests/integration/test_web_selenium.py diff --git a/autogpts/autogpt/autogpt/core/runner/cli_web_app/server/__init__.py b/autogpt/tests/mocks/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/core/runner/cli_web_app/server/__init__.py rename to autogpt/tests/mocks/__init__.py diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/__init__.py b/autogpt/tests/unit/__init__.py similarity index 100% rename from autogpts/autogpt/autogpt/core/runner/client_lib/__init__.py rename to autogpt/tests/unit/__init__.py diff --git a/autogpts/autogpt/tests/unit/data/test_ai_config.yaml b/autogpt/tests/unit/data/test_ai_config.yaml similarity index 100% rename from autogpts/autogpt/tests/unit/data/test_ai_config.yaml rename to autogpt/tests/unit/data/test_ai_config.yaml diff --git a/autogpts/autogpt/tests/unit/test_code_flow_strategy.py b/autogpt/tests/unit/test_code_flow_strategy.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_code_flow_strategy.py rename to autogpt/tests/unit/test_code_flow_strategy.py diff --git a/autogpts/autogpt/tests/unit/test_config.py b/autogpt/tests/unit/test_config.py similarity index 69% rename from autogpts/autogpt/tests/unit/test_config.py rename to autogpt/tests/unit/test_config.py index f5f6aa2fe3..efb8e588d3 100644 --- a/autogpts/autogpt/tests/unit/test_config.py +++ b/autogpt/tests/unit/test_config.py @@ -8,13 +8,12 @@ from typing import Any from unittest import mock import pytest -from forge.config.config import Config, ConfigBuilder -from forge.llm.providers.schema import ChatModelInfo, ModelProviderName +from forge.config.config import GPT_3_MODEL, GPT_4_MODEL, Config, ConfigBuilder from openai.pagination import AsyncPage from openai.types import Model from pydantic import SecretStr -from autogpt.app.configurator import GPT_3_MODEL, GPT_4_MODEL, apply_overrides_to_config +from autogpt.app.configurator import apply_overrides_to_config def test_initial_values(config: Config) -> None: @@ -46,11 +45,7 @@ async def test_fallback_to_gpt3_if_gpt4_not_available( ) ) - await apply_overrides_to_config( - config=config, - gpt3only=False, - gpt4only=False, - ) + await apply_overrides_to_config(config=config) assert config.fast_llm == GPT_3_MODEL assert config.smart_llm == GPT_3_MODEL @@ -67,8 +62,8 @@ def test_missing_azure_config(config: Config) -> None: with pytest.raises(ValueError): config.openai_credentials.load_azure_config(config_file) - assert config.openai_credentials.api_type != "azure" - assert config.openai_credentials.api_version == "" + assert config.openai_credentials.api_type != SecretStr("azure") + assert config.openai_credentials.api_version is None assert config.openai_credentials.azure_model_to_deploy_id_map is None @@ -98,8 +93,8 @@ azure_model_map: def test_azure_config(config_with_azure: Config) -> None: assert (credentials := config_with_azure.openai_credentials) is not None - assert credentials.api_type == "azure" - assert credentials.api_version == "2023-06-01-preview" + assert credentials.api_type == SecretStr("azure") + assert credentials.api_version == SecretStr("2023-06-01-preview") assert credentials.azure_endpoint == SecretStr("https://dummy.openai.azure.com") assert credentials.azure_model_to_deploy_id_map == { config_with_azure.fast_llm: "FAST-LLM_ID", @@ -139,43 +134,3 @@ def test_azure_config(config_with_azure: Config) -> None: credentials.get_model_access_kwargs(config_with_azure.smart_llm)["model"] == "FAST-LLM_ID" ) - - -@pytest.mark.asyncio -async def test_create_config_gpt4only(config: Config) -> None: - with mock.patch( - "forge.llm.providers.multi.MultiProvider.get_available_models" - ) as mock_get_models: - mock_get_models.return_value = [ - ChatModelInfo( - name=GPT_4_MODEL, - provider_name=ModelProviderName.OPENAI, - max_tokens=4096, - ) - ] - await apply_overrides_to_config( - config=config, - gpt4only=True, - ) - assert config.fast_llm == GPT_4_MODEL - assert config.smart_llm == GPT_4_MODEL - - -@pytest.mark.asyncio -async def test_create_config_gpt3only(config: Config) -> None: - with mock.patch( - "forge.llm.providers.multi.MultiProvider.get_available_models" - ) as mock_get_models: - mock_get_models.return_value = [ - ChatModelInfo( - name=GPT_3_MODEL, - provider_name=ModelProviderName.OPENAI, - max_tokens=4096, - ) - ] - await apply_overrides_to_config( - config=config, - gpt3only=True, - ) - assert config.fast_llm == GPT_3_MODEL - assert config.smart_llm == GPT_3_MODEL diff --git a/autogpts/autogpt/tests/unit/test_file_operations.py b/autogpt/tests/unit/test_file_operations.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_file_operations.py rename to autogpt/tests/unit/test_file_operations.py diff --git a/autogpts/autogpt/tests/unit/test_function_code_validation.py b/autogpt/tests/unit/test_function_code_validation.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_function_code_validation.py rename to autogpt/tests/unit/test_function_code_validation.py diff --git a/autogpts/autogpt/tests/unit/test_gcs_file_storage.py b/autogpt/tests/unit/test_gcs_file_storage.py similarity index 96% rename from autogpts/autogpt/tests/unit/test_gcs_file_storage.py rename to autogpt/tests/unit/test_gcs_file_storage.py index a4f091ef1a..d10c48988d 100644 --- a/autogpts/autogpt/tests/unit/test_gcs_file_storage.py +++ b/autogpt/tests/unit/test_gcs_file_storage.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest import pytest_asyncio -from forge.file_storage import GCSFileStorage, GCSFileStorageConfiguration +from forge.file_storage.gcs import GCSFileStorage, GCSFileStorageConfiguration from google.auth.exceptions import GoogleAuthError from google.cloud import storage from google.cloud.exceptions import NotFound @@ -14,6 +14,8 @@ try: except GoogleAuthError: pytest.skip("Google Cloud Authentication not configured", allow_module_level=True) +pytestmark = pytest.mark.slow + @pytest.fixture(scope="module") def gcs_bucket_name() -> str: @@ -26,7 +28,7 @@ def gcs_root() -> Path: @pytest.fixture(scope="module") -def gcs_storage_uninitialized(gcs_bucket_name: str, gcs_root: Path) -> GCSFileStorage: +def gcs_storage_uninitialized(gcs_bucket_name: str, gcs_root: Path): os.environ["STORAGE_BUCKET"] = gcs_bucket_name storage_config = GCSFileStorageConfiguration.from_env() storage_config.root = gcs_root @@ -52,7 +54,7 @@ def test_initialize(gcs_bucket_name: str, gcs_storage_uninitialized: GCSFileStor @pytest.fixture(scope="module") -def gcs_storage(gcs_storage_uninitialized: GCSFileStorage) -> GCSFileStorage: +def gcs_storage(gcs_storage_uninitialized: GCSFileStorage): (gcs_storage := gcs_storage_uninitialized).initialize() yield gcs_storage # type: ignore @@ -77,7 +79,7 @@ TEST_FILES: list[tuple[str | Path, str]] = [ @pytest_asyncio.fixture -async def gcs_storage_with_files(gcs_storage: GCSFileStorage) -> GCSFileStorage: +async def gcs_storage_with_files(gcs_storage: GCSFileStorage): for file_name, file_content in TEST_FILES: gcs_storage._bucket.blob( str(gcs_storage.get_path(file_name)) diff --git a/autogpts/autogpt/tests/unit/test_git_commands.py b/autogpt/tests/unit/test_git_commands.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_git_commands.py rename to autogpt/tests/unit/test_git_commands.py diff --git a/autogpts/autogpt/tests/unit/test_json.py b/autogpt/tests/unit/test_json.py similarity index 97% rename from autogpts/autogpt/tests/unit/test_json.py rename to autogpt/tests/unit/test_json.py index 62f8b9690a..9067238838 100644 --- a/autogpts/autogpt/tests/unit/test_json.py +++ b/autogpt/tests/unit/test_json.py @@ -1,7 +1,7 @@ import json import pytest -from forge.json import json_loads +from forge.json.parsing import json_loads _JSON_FIXABLE: list[tuple[str, str]] = [ # Missing comma @@ -31,7 +31,7 @@ _JSON_FIXABLE: list[tuple[str, str]] = [ '```json\n{"name": "John Doe", "age": 30}\n```', '{"name": "John Doe", "age": 30}', ), - # Mutliple problems + # Multiple problems ( '{"name":"John Doe" "age": 30\n "empty": "","address": ' "// random comment\n" diff --git a/autogpts/autogpt/tests/unit/test_local_file_storage.py b/autogpt/tests/unit/test_local_file_storage.py similarity index 98% rename from autogpts/autogpt/tests/unit/test_local_file_storage.py rename to autogpt/tests/unit/test_local_file_storage.py index 8f4648c4ec..930e562e4c 100644 --- a/autogpts/autogpt/tests/unit/test_local_file_storage.py +++ b/autogpt/tests/unit/test_local_file_storage.py @@ -1,7 +1,7 @@ from pathlib import Path import pytest -from forge.file_storage import FileStorageConfiguration, LocalFileStorage +from forge.file_storage.local import FileStorageConfiguration, LocalFileStorage _ACCESSIBLE_PATHS = [ Path("."), diff --git a/autogpts/autogpt/tests/unit/test_logs.py b/autogpt/tests/unit/test_logs.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_logs.py rename to autogpt/tests/unit/test_logs.py diff --git a/autogpts/autogpt/tests/unit/test_s3_file_storage.py b/autogpt/tests/unit/test_s3_file_storage.py similarity index 93% rename from autogpts/autogpt/tests/unit/test_s3_file_storage.py rename to autogpt/tests/unit/test_s3_file_storage.py index fc88cd3556..3a40ad8f7f 100644 --- a/autogpts/autogpt/tests/unit/test_s3_file_storage.py +++ b/autogpt/tests/unit/test_s3_file_storage.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest import pytest_asyncio from botocore.exceptions import ClientError -from forge.file_storage import S3FileStorage, S3FileStorageConfiguration +from forge.file_storage.s3 import S3FileStorage, S3FileStorageConfiguration if not (os.getenv("S3_ENDPOINT_URL") and os.getenv("AWS_ACCESS_KEY_ID")): pytest.skip("S3 environment variables are not set", allow_module_level=True) @@ -22,7 +22,7 @@ def s3_root() -> Path: @pytest.fixture -def s3_storage_uninitialized(s3_bucket_name: str, s3_root: Path) -> S3FileStorage: +def s3_storage_uninitialized(s3_bucket_name: str, s3_root: Path): os.environ["STORAGE_BUCKET"] = s3_bucket_name storage_config = S3FileStorageConfiguration.from_env() storage_config.root = s3_root @@ -36,12 +36,13 @@ def test_initialize(s3_bucket_name: str, s3_storage_uninitialized: S3FileStorage # test that the bucket doesn't exist yet with pytest.raises(ClientError): - s3.meta.client.head_bucket(Bucket=s3_bucket_name) + s3.meta.client.head_bucket(Bucket=s3_bucket_name) # pyright: ignore s3_storage_uninitialized.initialize() # test that the bucket has been created - s3.meta.client.head_bucket(Bucket=s3_bucket_name) + s3.meta.client.head_bucket(Bucket=s3_bucket_name) # pyright: ignore + # FIXME: remove the "pyright: ignore" comments after moving this test file to forge def test_workspace_bucket_name( @@ -52,7 +53,7 @@ def test_workspace_bucket_name( @pytest.fixture -def s3_storage(s3_storage_uninitialized: S3FileStorage) -> S3FileStorage: +def s3_storage(s3_storage_uninitialized: S3FileStorage): (s3_storage := s3_storage_uninitialized).initialize() yield s3_storage # type: ignore @@ -71,7 +72,7 @@ TEST_FILES: list[tuple[str | Path, str]] = [ @pytest_asyncio.fixture -async def s3_storage_with_files(s3_storage: S3FileStorage) -> S3FileStorage: +async def s3_storage_with_files(s3_storage: S3FileStorage): for file_name, file_content in TEST_FILES: s3_storage._bucket.Object(str(s3_storage.get_path(file_name))).put( Body=file_content diff --git a/autogpts/autogpt/tests/unit/test_spinner.py b/autogpt/tests/unit/test_spinner.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_spinner.py rename to autogpt/tests/unit/test_spinner.py diff --git a/autogpts/autogpt/tests/unit/test_text_file_parsers.py b/autogpt/tests/unit/test_text_file_parsers.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_text_file_parsers.py rename to autogpt/tests/unit/test_text_file_parsers.py diff --git a/autogpts/autogpt/tests/unit/test_url_validation.py b/autogpt/tests/unit/test_url_validation.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_url_validation.py rename to autogpt/tests/unit/test_url_validation.py diff --git a/autogpts/autogpt/tests/unit/test_utils.py b/autogpt/tests/unit/test_utils.py similarity index 88% rename from autogpts/autogpt/tests/unit/test_utils.py rename to autogpt/tests/unit/test_utils.py index 9fce404852..7b9b93c10c 100644 --- a/autogpts/autogpt/tests/unit/test_utils.py +++ b/autogpt/tests/unit/test_utils.py @@ -6,7 +6,6 @@ from unittest.mock import patch import pytest import requests from forge.json.parsing import extract_dict_from_json -from forge.utils.yaml_validator import validate_yaml_file from git import InvalidGitRepositoryError import autogpt.app.utils @@ -58,41 +57,6 @@ def invalid_json_response() -> dict: } -def test_validate_yaml_file_valid(): - with open("valid_test_file.yaml", "w") as f: - f.write("setting: value") - result, message = validate_yaml_file("valid_test_file.yaml") - os.remove("valid_test_file.yaml") - - assert result is True - assert "Successfully validated" in message - - -def test_validate_yaml_file_not_found(): - result, message = validate_yaml_file("non_existent_file.yaml") - - assert result is False - assert "wasn't found" in message - - -def test_validate_yaml_file_invalid(): - with open("invalid_test_file.yaml", "w") as f: - f.write( - "settings:\n" - " first_setting: value\n" - " second_setting: value\n" - " nested_setting: value\n" - " third_setting: value\n" - "unindented_setting: value" - ) - result, message = validate_yaml_file("invalid_test_file.yaml") - os.remove("invalid_test_file.yaml") - print(result) - print(message) - assert result is False - assert "There was an issue while trying to read" in message - - @patch("requests.get") def test_get_bulletin_from_web_success(mock_get): expected_content = "Test bulletin from web" @@ -103,7 +67,7 @@ def test_get_bulletin_from_web_success(mock_get): assert expected_content in bulletin mock_get.assert_called_with( - "https://raw.githubusercontent.com/Significant-Gravitas/AutoGPT/master/autogpts/autogpt/BULLETIN.md" # noqa: E501 + "https://raw.githubusercontent.com/Significant-Gravitas/AutoGPT/master/autogpt/BULLETIN.md" # noqa: E501 ) diff --git a/autogpts/autogpt/tests/unit/test_web_search.py b/autogpt/tests/unit/test_web_search.py similarity index 100% rename from autogpts/autogpt/tests/unit/test_web_search.py rename to autogpt/tests/unit/test_web_search.py diff --git a/autogpts/autogpt/tests/utils.py b/autogpt/tests/utils.py similarity index 100% rename from autogpts/autogpt/tests/utils.py rename to autogpt/tests/utils.py diff --git a/autogpts/autogpt/tests/vcr/__init__.py b/autogpt/tests/vcr/__init__.py similarity index 94% rename from autogpts/autogpt/tests/vcr/__init__.py rename to autogpt/tests/vcr/__init__.py index 8d477cfe28..9c31e95a4c 100644 --- a/autogpts/autogpt/tests/vcr/__init__.py +++ b/autogpt/tests/vcr/__init__.py @@ -1,6 +1,7 @@ import logging import os from hashlib import sha256 +from typing import cast import pytest from openai import OpenAI @@ -53,11 +54,14 @@ def cached_openai_client(mocker: MockerFixture) -> OpenAI: def _patched_prepare_options(self, options: FinalRequestOptions): _prepare_options(options) + if not options.json_data: + return + headers: dict[str, str | Omit] = ( {**options.headers} if is_given(options.headers) else {} ) options.headers = headers - data: dict = options.json_data + data = cast(dict, options.json_data) logging.getLogger("cached_openai_client").debug( f"Outgoing API request: {headers}\n{data if data else None}" diff --git a/autogpts/autogpt/tests/vcr/vcr_filter.py b/autogpt/tests/vcr/vcr_filter.py similarity index 100% rename from autogpts/autogpt/tests/vcr/vcr_filter.py rename to autogpt/tests/vcr/vcr_filter.py diff --git a/autogpts/autogpt/tests/vcr_cassettes b/autogpt/tests/vcr_cassettes similarity index 100% rename from autogpts/autogpt/tests/vcr_cassettes rename to autogpt/tests/vcr_cassettes diff --git a/autogpts/autogpt/.dockerignore b/autogpts/autogpt/.dockerignore deleted file mode 100644 index 1ee35738b6..0000000000 --- a/autogpts/autogpt/.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -.* -**/.venv* -**/__pycache__ -*.template -*.yaml -*.yml -!prompt_settings.yaml - -data/* -logs/* -agbenchmark_config/logs/* -agbenchmark_config/reports/* - -*.png diff --git a/autogpts/autogpt/.flake8 b/autogpts/autogpt/.flake8 deleted file mode 100644 index b9aa1bc730..0000000000 --- a/autogpts/autogpt/.flake8 +++ /dev/null @@ -1,11 +0,0 @@ -[flake8] -max-line-length = 88 -extend-exclude = - .*_cache/, - .venv, - data/, - logs/, - tests/unit/data/, -extend-ignore = - # No whitespace before ':' conflicts with Black style for slices - E203, diff --git a/autogpts/autogpt/.pre-commit-config.yaml b/autogpts/autogpt/.pre-commit-config.yaml deleted file mode 100644 index 955d49fa3f..0000000000 --- a/autogpts/autogpt/.pre-commit-config.yaml +++ /dev/null @@ -1,47 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-added-large-files - args: ['--maxkb=500'] - - id: check-byte-order-marker - - id: check-case-conflict - - id: check-merge-conflict - - id: check-symlinks - - id: debug-statements - - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - language_version: python3.10 - - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - language_version: python3.10 - - - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - - # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: 'v1.3.0' - # hooks: - # - id: mypy - - - repo: local - hooks: - # - id: autoflake - # name: autoflake - # entry: autoflake --in-place --remove-all-unused-imports --recursive --ignore-init-module-imports --ignore-pass-after-docstring autogpt tests - # language: python - # types: [ python ] - - id: pytest-check - name: pytest-check - entry: bash -c 'cd autogpts/autogpt && poetry run pytest --cov=autogpt tests/unit' - language: system - pass_filenames: false - always_run: true diff --git a/autogpts/autogpt/agbenchmark_config/benchmarks.py b/autogpts/autogpt/agbenchmark_config/benchmarks.py deleted file mode 100644 index 7f60774fd2..0000000000 --- a/autogpts/autogpt/agbenchmark_config/benchmarks.py +++ /dev/null @@ -1,78 +0,0 @@ -import asyncio -import logging -import sys -from pathlib import Path - -from forge.config.ai_profile import AIProfile -from forge.config.config import ConfigBuilder -from forge.file_storage import FileStorageBackendName, get_storage -from forge.logging.config import configure_logging - -from autogpt.agent_manager.agent_manager import AgentManager -from autogpt.agents.agent import Agent, AgentConfiguration, AgentSettings -from autogpt.app.main import _configure_llm_provider, run_interaction_loop - -LOG_DIR = Path(__file__).parent / "logs" - - -def run_specific_agent(task: str, continuous_mode: bool = False) -> None: - agent = bootstrap_agent(task, continuous_mode) - asyncio.run(run_interaction_loop(agent)) - - -def bootstrap_agent(task: str, continuous_mode: bool) -> Agent: - configure_logging( - level=logging.DEBUG, - log_dir=LOG_DIR, - plain_console_output=True, - ) - - config = ConfigBuilder.build_config_from_env() - config.continuous_mode = continuous_mode - config.continuous_limit = 20 - config.noninteractive_mode = True - config.memory_backend = "no_memory" - - ai_profile = AIProfile( - ai_name="AutoGPT", - ai_role="a multi-purpose AI assistant.", - ai_goals=[task], - ) - - agent_settings = AgentSettings( - name=Agent.default_settings.name, - agent_id=AgentManager.generate_id("AutoGPT-benchmark"), - description=Agent.default_settings.description, - ai_profile=ai_profile, - config=AgentConfiguration( - fast_llm=config.fast_llm, - smart_llm=config.smart_llm, - allow_fs_access=not config.restrict_to_workspace, - use_functions_api=config.openai_functions, - ), - history=Agent.default_settings.history.copy(deep=True), - ) - - local = config.file_storage_backend == FileStorageBackendName.LOCAL - restrict_to_root = not local or config.restrict_to_workspace - file_storage = get_storage( - config.file_storage_backend, root_path="data", restrict_to_root=restrict_to_root - ) - file_storage.initialize() - - agent = Agent( - settings=agent_settings, - llm_provider=_configure_llm_provider(config), - file_storage=file_storage, - legacy_config=config, - ) - return agent - - -if __name__ == "__main__": - # The first argument is the script name itself, second is the task - if len(sys.argv) != 2: - print("Usage: python script.py ") - sys.exit(1) - task = sys.argv[1] - run_specific_agent(task, continuous_mode=True) diff --git a/autogpts/autogpt/autogpt/agent_manager/__init__.py b/autogpts/autogpt/autogpt/agent_manager/__init__.py deleted file mode 100644 index a412566bf3..0000000000 --- a/autogpts/autogpt/autogpt/agent_manager/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .agent_manager import AgentManager - -__all__ = ["AgentManager"] diff --git a/autogpts/autogpt/autogpt/core/ARCHITECTURE_NOTES.md b/autogpts/autogpt/autogpt/core/ARCHITECTURE_NOTES.md deleted file mode 100644 index af6aac7b78..0000000000 --- a/autogpts/autogpt/autogpt/core/ARCHITECTURE_NOTES.md +++ /dev/null @@ -1,271 +0,0 @@ -# Re-architecture Notes - -## Key Documents - -- [Planned Agent Workflow](https://whimsical.com/agent-workflow-v2-NmnTQ8R7sVo7M3S43XgXmZ) -- [Original Architecture Diagram](https://www.figma.com/file/fwdj44tPR7ArYtnGGUKknw/Modular-Architecture?type=whiteboard&node-id=0-1) - This is sadly well out of date at this point. -- [Kanban](https://github.com/orgs/Significant-Gravitas/projects/1/views/1?filterQuery=label%3Are-arch) - -## The Motivation - -The `master` branch of AutoGPT is an organically grown amalgamation of many thoughts -and ideas about agent-driven autonomous systems. It lacks clear abstraction boundaries, -has issues of global state and poorly encapsulated state, and is generally just hard to -make effective changes to. Mainly it's just a system that's hard to make changes to. -And research in the field is moving fast, so we want to be able to try new ideas -quickly. - -## Initial Planning - -A large group of maintainers and contributors met do discuss the architectural -challenges associated with the existing codebase. Many much-desired features (building -new user interfaces, enabling project-specific agents, enabling multi-agent systems) -are bottlenecked by the global state in the system. We discussed the tradeoffs between -an incremental system transition and a big breaking version change and decided to go -for the breaking version change. We justified this by saying: - -- We can maintain, in essence, the same user experience as now even with a radical - restructuring of the codebase -- Our developer audience is struggling to use the existing codebase to build - applications and libraries of their own, so this breaking change will largely be - welcome. - -## Primary Goals - -- Separate the AutoGPT application code from the library code. -- Remove global state from the system -- Allow for multiple agents per user (with facilities for running simultaneously) -- Create a serializable representation of an Agent -- Encapsulate the core systems in abstractions with clear boundaries. - -## Secondary goals - -- Use existing tools to ditch any unnecessary cruft in the codebase (document loading, - json parsing, anything easier to replace than to port). -- Bring in the [core agent loop updates](https://whimsical.com/agent-workflow-v2-NmnTQ8R7sVo7M3S43XgXmZ) - being developed simultaneously by @Pwuts - -# The Agent Subsystems - -## Configuration - -We want a lot of things from a configuration system. We lean heavily on it in the -`master` branch to allow several parts of the system to communicate with each other. -[Recent work](https://github.com/Significant-Gravitas/AutoGPT/pull/4737) has made it -so that the config is no longer a singleton object that is materialized from the import -state, but it's still treated as a -[god object](https://en.wikipedia.org/wiki/God_object) containing all information about -the system and _critically_ allowing any system to reference configuration information -about other parts of the system. - -### What we want - -- It should still be reasonable to collate the entire system configuration in a - sensible way. -- The configuration should be validatable and validated. -- The system configuration should be a _serializable_ representation of an `Agent`. -- The configuration system should provide a clear (albeit very low-level) contract - about user-configurable aspects of the system. -- The configuration should reasonably manage default values and user-provided overrides. -- The configuration system needs to handle credentials in a reasonable way. -- The configuration should be the representation of some amount of system state, like - api budgets and resource usage. These aspects are recorded in the configuration and - updated by the system itself. -- Agent systems should have encapsulated views of the configuration. E.g. the memory - system should know about memory configuration but nothing about command configuration. - -## Workspace - -There are two ways to think about the workspace: - -- The workspace is a scratch space for an agent where it can store files, write code, - and do pretty much whatever else it likes. -- The workspace is, at any given point in time, the single source of truth for what an - agent is. It contains the serializable state (the configuration) as well as all - other working state (stored files, databases, memories, custom code). - -In the existing system there is **one** workspace. And because the workspace holds so -much agent state, that means a user can only work with one agent at a time. - -## Memory - -The memory system has been under extremely active development. -See [#3536](https://github.com/Significant-Gravitas/AutoGPT/issues/3536) and -[#4208](https://github.com/Significant-Gravitas/AutoGPT/pull/4208) for discussion and -work in the `master` branch. The TL;DR is -that we noticed a couple of months ago that the `Agent` performed **worse** with -permanent memory than without it. Since then the knowledge storage and retrieval -system has been [redesigned](https://whimsical.com/memory-system-8Ae6x6QkjDwQAUe9eVJ6w1) -and partially implemented in the `master` branch. - -## Planning/Prompt-Engineering - -The planning system is the system that translates user desires/agent intentions into -language model prompts. In the course of development, it has become pretty clear -that `Planning` is the wrong name for this system - -### What we want - -- It should be incredibly obvious what's being passed to a language model, when it's - being passed, and what the language model response is. The landscape of language - model research is developing very rapidly, so building complex abstractions between - users/contributors and the language model interactions is going to make it very - difficult for us to nimbly respond to new research developments. -- Prompt-engineering should ideally be exposed in a parameterizeable way to users. -- We should, where possible, leverage OpenAI's new - [function calling api](https://openai.com/blog/function-calling-and-other-api-updates) - to get outputs in a standard machine-readable format and avoid the deep pit of - parsing json (and fixing unparsable json). - -### Planning Strategies - -The [new agent workflow](https://whimsical.com/agent-workflow-v2-NmnTQ8R7sVo7M3S43XgXmZ) -has many, many interaction points for language models. We really would like to not -distribute prompt templates and raw strings all through the system. The re-arch solution -is to encapsulate language model interactions into planning strategies. -These strategies are defined by - -- The `LanguageModelClassification` they use (`FAST` or `SMART`) -- A function `build_prompt` that takes strategy specific arguments and constructs a - `LanguageModelPrompt` (a simple container for lists of messages and functions to - pass to the language model) -- A function `parse_content` that parses the response content (a dict) into a better - formatted dict. Contracts here are intentionally loose and will tighten once we have - at least one other language model provider. - -## Resources - -Resources are kinds of services we consume from external APIs. They may have associated -credentials and costs we need to manage. Management of those credentials is implemented -as manipulation of the resource configuration. We have two categories of resources -currently - -- AI/ML model providers (including language model providers and embedding model providers, ie OpenAI) -- Memory providers (e.g. Pinecone, Weaviate, ChromaDB, etc.) - -### What we want - -- Resource abstractions should provide a common interface to different service providers - for a particular kind of service. -- Resource abstractions should manipulate the configuration to manage their credentials - and budget/accounting. -- Resource abstractions should be composable over an API (e.g. I should be able to make - an OpenAI provider that is both a LanguageModelProvider and an EmbeddingModelProvider - and use it wherever I need those services). - -## Abilities - -Along with planning and memory usage, abilities are one of the major augmentations of -augmented language models. They allow us to expand the scope of what language models -can do by hooking them up to code they can execute to obtain new knowledge or influence -the world. - -### What we want - -- Abilities should have an extremely clear interface that users can write to. -- Abilities should have an extremely clear interface that a language model can - understand -- Abilities should be declarative about their dependencies so the system can inject them -- Abilities should be executable (where sensible) in an async run loop. -- Abilities should be not have side effects unless those side effects are clear in - their representation to an agent (e.g. the BrowseWeb ability shouldn't write a file, - but the WriteFile ability can). - -## Plugins - -Users want to add lots of features that we don't want to support as first-party. -Or solution to this is a plugin system to allow users to plug in their functionality or -to construct their agent from a public plugin marketplace. Our primary concern in the -re-arch is to build a stateless plugin service interface and a simple implementation -that can load plugins from installed packages or from zip files. Future efforts will -expand this system to allow plugins to load from a marketplace or some other kind -of service. - -### What is a Plugin - -Plugins are a kind of garbage term. They refer to a number of things. - -- New commands for the agent to execute. This is the most common usage. -- Replacements for entire subsystems like memory or language model providers -- Application plugins that do things like send emails or communicate via whatsapp -- The repositories contributors create that may themselves have multiple plugins in them. - -### Usage in the existing system - -The current plugin system is _hook-based_. This means plugins don't correspond to -kinds of objects in the system, but rather to times in the system at which we defer -execution to them. The main advantage of this setup is that user code can hijack -pretty much any behavior of the agent by injecting code that supersedes the normal -agent execution. The disadvantages to this approach are numerous: - -- We have absolutely no mechanisms to enforce any security measures because the threat - surface is everything. -- We cannot reason about agent behavior in a cohesive way because control flow can be - ceded to user code at pretty much any point and arbitrarily change or break the - agent behavior -- The interface for designing a plugin is kind of terrible and difficult to standardize -- The hook based implementation means we couple ourselves to a particular flow of - control (or otherwise risk breaking plugin behavior). E.g. many of the hook targets - in the [old workflow](https://whimsical.com/agent-workflow-VAzeKcup3SR7awpNZJKTyK) - are not present or mean something entirely different in the - [new workflow](https://whimsical.com/agent-workflow-v2-NmnTQ8R7sVo7M3S43XgXmZ). -- Etc. - -### What we want - -- A concrete definition of a plugin that is narrow enough in scope that we can define - it well and reason about how it will work in the system. -- A set of abstractions that let us define a plugin by its storage format and location -- A service interface that knows how to parse the plugin abstractions and turn them - into concrete classes and objects. - - -## Some Notes on how and why we'll use OO in this project - -First and foremost, Python itself is an object-oriented language. It's -underlying [data model](https://docs.python.org/3/reference/datamodel.html) is built -with object-oriented programming in mind. It offers useful tools like abstract base -classes to communicate interfaces to developers who want to, e.g., write plugins, or -help work on implementations. If we were working in a different language that offered -different tools, we'd use a different paradigm. - -While many things are classes in the re-arch, they are not classes in the same way. -There are three kinds of things (roughly) that are written as classes in the re-arch: -1. **Configuration**: AutoGPT has *a lot* of configuration. This configuration - is *data* and we use **[Pydantic](https://docs.pydantic.dev/latest/)** to manage it as - pydantic is basically industry standard for this stuff. It provides runtime validation - for all the configuration and allows us to easily serialize configuration to both basic - python types (dicts, lists, and primitives) as well as serialize to json, which is - important for us being able to put representations of agents - [on the wire](https://en.wikipedia.org/wiki/Wire_protocol) for web applications and - agent-to-agent communication. *These are essentially - [structs](https://en.wikipedia.org/wiki/Struct_(C_programming_language)) rather than - traditional classes.* -2. **Internal Data**: Very similar to configuration, AutoGPT passes around boatloads - of internal data. We are interacting with language models and language model APIs - which means we are handling lots of *structured* but *raw* text. Here we also - leverage **pydantic** to both *parse* and *validate* the internal data and also to - give us concrete types which we can use static type checkers to validate against - and discover problems before they show up as bugs at runtime. *These are - essentially [structs](https://en.wikipedia.org/wiki/Struct_(C_programming_language)) - rather than traditional classes.* -3. **System Interfaces**: This is our primary traditional use of classes in the - re-arch. We have a bunch of systems. We want many of those systems to have - alternative implementations (e.g. via plugins). We use abstract base classes to - define interfaces to communicate with people who might want to provide those - plugins. We provide a single concrete implementation of most of those systems as a - subclass of the interface. This should not be controversial. - -The approach is consistent with -[prior](https://github.com/Significant-Gravitas/AutoGPT/issues/2458) -[work](https://github.com/Significant-Gravitas/AutoGPT/pull/2442) done by other -maintainers in this direction. - -From an organization standpoint, OO programming is by far the most popular programming -paradigm (especially for Python). It's the one most often taught in programming classes -and the one with the most available online training for people interested in -contributing. - -Finally, and importantly, we scoped the plan and initial design of the re-arch as a -large group of maintainers and collaborators early on. This is consistent with the -design we chose and no-one offered alternatives. diff --git a/autogpts/autogpt/autogpt/core/README.md b/autogpts/autogpt/autogpt/core/README.md deleted file mode 100644 index ff97e2c59f..0000000000 --- a/autogpts/autogpt/autogpt/core/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# AutoGPT Core - -This subpackage contains the ongoing work for the -[AutoGPT Re-arch](https://github.com/Significant-Gravitas/AutoGPT/issues/4770). It is -a work in progress and is not yet feature complete. In particular, it does not yet -have many of the AutoGPT commands implemented and is pending ongoing work to -[re-incorporate vector-based memory and knowledge retrieval](https://github.com/Significant-Gravitas/AutoGPT/issues/3536). - -## [Overview](ARCHITECTURE_NOTES.md) - -The AutoGPT Re-arch is a re-implementation of the AutoGPT agent that is designed to be more modular, -more extensible, and more maintainable than the original AutoGPT agent. It is also designed to be -more accessible to new developers and to be easier to contribute to. The re-arch is a work in progress -and is not yet feature complete. It is also not yet ready for production use. - -## Running the Re-arch Code - -1. Open the `autogpt/core` folder in a terminal - -2. Set up a dedicated virtual environment: - `python -m venv .venv` - -3. Install everything needed to run the project: - `poetry install` - - -## CLI Application - -There are two client applications for AutoGPT included. - -:star2: **This is the reference application I'm working with for now** :star2: - -The first app is a straight CLI application. I have not done anything yet to port all the friendly display stuff from the ~~`logger.typewriter_log`~~`user_friendly_output` logic. - -- [Entry Point](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/autogpt/core/runner/cli_app/cli.py) -- [Client Application](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/autogpt/core/runner/cli_app/main.py) - -You'll then need a settings file. Run - -``` -poetry run cli make-settings -``` - -This will write a file called `default_agent_settings.yaml` with all the user-modifiable -configuration keys to `~/auto-gpt/default_agent_settings.yml` and make the `auto-gpt` directory -in your user directory if it doesn't exist). Your user directory is located in different places -depending on your operating system: - -- On Linux, it's `/home/USERNAME` -- On Windows, it's `C:\Users\USERNAME` -- On Mac, it's `/Users/USERNAME` - -At a bare minimum, you'll need to set `openai.credentials.api_key` to your OpenAI API Key to run -the model. - -You can then run AutoGPT with - -``` -poetry run cli run -``` - -to launch the interaction loop. - -### CLI Web App - -:warning: I am not actively developing this application. I am primarily working with the traditional CLI app -described above. It is a very good place to get involved if you have web application design experience and are -looking to get involved in the re-arch. - -The second app is still a CLI, but it sets up a local webserver that the client application talks to -rather than invoking calls to the Agent library code directly. This application is essentially a sketch -at this point as the folks who were driving it have had less time (and likely not enough clarity) to proceed. - -- [Entry Point](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/autogpt/core/runner/cli_web_app/cli.py) -- [Client Application](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/autogpt/core/runner/cli_web_app/client/client.py) -- [Server API](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/autogpt/core/runner/cli_web_app/server/api.py) - -To run, you still need to generate a default configuration. You can do - -``` -poetry run cli-web make-settings -``` - -It invokes the same command as the bare CLI app, so follow the instructions above about setting your API key. - -To run, do - -``` -poetry run cli-web client -``` - -This will launch a webserver and then start the client cli application to communicate with it. diff --git a/autogpts/autogpt/autogpt/core/ability/__init__.py b/autogpts/autogpt/autogpt/core/ability/__init__.py deleted file mode 100644 index 3709b9277a..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""The command system provides a way to extend the functionality of the AI agent.""" -from autogpt.core.ability.base import Ability, AbilityConfiguration, AbilityRegistry -from autogpt.core.ability.schema import AbilityResult -from autogpt.core.ability.simple import ( - AbilityRegistryConfiguration, - AbilityRegistrySettings, - SimpleAbilityRegistry, -) - -__all__ = [ - "Ability", - "AbilityConfiguration", - "AbilityRegistry", - "AbilityResult", - "AbilityRegistryConfiguration", - "AbilityRegistrySettings", - "SimpleAbilityRegistry", -] diff --git a/autogpts/autogpt/autogpt/core/ability/base.py b/autogpts/autogpt/autogpt/core/ability/base.py deleted file mode 100644 index d6acd87013..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/base.py +++ /dev/null @@ -1,88 +0,0 @@ -import abc -from pprint import pformat -from typing import Any, ClassVar - -import inflection -from forge.llm.providers import CompletionModelFunction -from forge.models.config import SystemConfiguration -from forge.models.json_schema import JSONSchema -from pydantic import Field - -from autogpt.core.planning.simple import LanguageModelConfiguration -from autogpt.core.plugin.base import PluginLocation - -from .schema import AbilityResult - - -class AbilityConfiguration(SystemConfiguration): - """Struct for model configuration.""" - - location: PluginLocation - packages_required: list[str] = Field(default_factory=list) - language_model_required: LanguageModelConfiguration = None - memory_provider_required: bool = False - workspace_required: bool = False - - -class Ability(abc.ABC): - """A class representing an agent ability.""" - - default_configuration: ClassVar[AbilityConfiguration] - - @classmethod - def name(cls) -> str: - """The name of the ability.""" - return inflection.underscore(cls.__name__) - - @property - @classmethod - @abc.abstractmethod - def description(cls) -> str: - """A detailed description of what the ability does.""" - ... - - @property - @classmethod - @abc.abstractmethod - def parameters(cls) -> dict[str, JSONSchema]: - ... - - @abc.abstractmethod - async def __call__(self, *args: Any, **kwargs: Any) -> AbilityResult: - ... - - def __str__(self) -> str: - return pformat(self.spec) - - @property - @classmethod - def spec(cls) -> CompletionModelFunction: - return CompletionModelFunction( - name=cls.name(), - description=cls.description, - parameters=cls.parameters, - ) - - -class AbilityRegistry(abc.ABC): - @abc.abstractmethod - def register_ability( - self, ability_name: str, ability_configuration: AbilityConfiguration - ) -> None: - ... - - @abc.abstractmethod - def list_abilities(self) -> list[str]: - ... - - @abc.abstractmethod - def dump_abilities(self) -> list[CompletionModelFunction]: - ... - - @abc.abstractmethod - def get_ability(self, ability_name: str) -> Ability: - ... - - @abc.abstractmethod - async def perform(self, ability_name: str, **kwargs: Any) -> AbilityResult: - ... diff --git a/autogpts/autogpt/autogpt/core/ability/builtins/__init__.py b/autogpts/autogpt/autogpt/core/ability/builtins/__init__.py deleted file mode 100644 index 93936dbc68..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/builtins/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from autogpt.core.ability.builtins.create_new_ability import CreateNewAbility -from autogpt.core.ability.builtins.query_language_model import QueryLanguageModel - -BUILTIN_ABILITIES = { - QueryLanguageModel.name(): QueryLanguageModel, -} - -__all__ = [ - "BUILTIN_ABILITIES", - "CreateNewAbility", - "QueryLanguageModel", -] diff --git a/autogpts/autogpt/autogpt/core/ability/builtins/create_new_ability.py b/autogpts/autogpt/autogpt/core/ability/builtins/create_new_ability.py deleted file mode 100644 index 110cff0145..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/builtins/create_new_ability.py +++ /dev/null @@ -1,108 +0,0 @@ -import logging -from typing import ClassVar - -from forge.models.json_schema import JSONSchema - -from autogpt.core.ability.base import Ability, AbilityConfiguration -from autogpt.core.ability.schema import AbilityResult -from autogpt.core.plugin.simple import PluginLocation, PluginStorageFormat - - -class CreateNewAbility(Ability): - default_configuration = AbilityConfiguration( - location=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.ability.builtins.CreateNewAbility", - ), - ) - - def __init__( - self, - logger: logging.Logger, - configuration: AbilityConfiguration, - ): - self._logger = logger - self._configuration = configuration - - description: ClassVar[str] = "Create a new ability by writing python code." - - parameters: ClassVar[dict[str, JSONSchema]] = { - "ability_name": JSONSchema( - description="A meaningful and concise name for the new ability.", - type=JSONSchema.Type.STRING, - required=True, - ), - "description": JSONSchema( - description=( - "A detailed description of the ability and its uses, " - "including any limitations." - ), - type=JSONSchema.Type.STRING, - required=True, - ), - "arguments": JSONSchema( - description="A list of arguments that the ability will accept.", - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - type=JSONSchema.Type.OBJECT, - properties={ - "name": JSONSchema( - description="The name of the argument.", - type=JSONSchema.Type.STRING, - ), - "type": JSONSchema( - description=( - "The type of the argument. " - "Must be a standard json schema type." - ), - type=JSONSchema.Type.STRING, - ), - "description": JSONSchema( - description=( - "A detailed description of the argument and its uses." - ), - type=JSONSchema.Type.STRING, - ), - }, - ), - ), - "required_arguments": JSONSchema( - description="A list of the names of the arguments that are required.", - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - description="The names of the arguments that are required.", - type=JSONSchema.Type.STRING, - ), - ), - "package_requirements": JSONSchema( - description=( - "A list of the names of the Python packages that are required to " - "execute the ability." - ), - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - description=( - "The of the Python package that is required to execute the ability." - ), - type=JSONSchema.Type.STRING, - ), - ), - "code": JSONSchema( - description=( - "The Python code that will be executed when the ability is called." - ), - type=JSONSchema.Type.STRING, - required=True, - ), - } - - async def __call__( - self, - ability_name: str, - description: str, - arguments: list[dict], - required_arguments: list[str], - package_requirements: list[str], - code: str, - ) -> AbilityResult: - raise NotImplementedError diff --git a/autogpts/autogpt/autogpt/core/ability/builtins/file_operations.py b/autogpts/autogpt/autogpt/core/ability/builtins/file_operations.py deleted file mode 100644 index 44b4d5f9c0..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/builtins/file_operations.py +++ /dev/null @@ -1,171 +0,0 @@ -import logging -import os -from typing import ClassVar - -from forge.models.json_schema import JSONSchema - -from autogpt.core.ability.base import Ability, AbilityConfiguration -from autogpt.core.ability.schema import AbilityResult, ContentType, Knowledge -from autogpt.core.plugin.simple import PluginLocation, PluginStorageFormat -from autogpt.core.workspace import Workspace - - -class ReadFile(Ability): - default_configuration = AbilityConfiguration( - location=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.ability.builtins.ReadFile", - ), - packages_required=["unstructured"], - workspace_required=True, - ) - - def __init__( - self, - logger: logging.Logger, - workspace: Workspace, - ): - self._logger = logger - self._workspace = workspace - - description: ClassVar[str] = "Read and parse all text from a file." - - parameters: ClassVar[dict[str, JSONSchema]] = { - "filename": JSONSchema( - type=JSONSchema.Type.STRING, - description="The name of the file to read.", - ), - } - - def _check_preconditions(self, filename: str) -> AbilityResult | None: - message = "" - try: - pass - except ImportError: - message = "Package charset_normalizer is not installed." - - try: - file_path = self._workspace.get_path(filename) - if not file_path.exists(): - message = f"File {filename} does not exist." - if not file_path.is_file(): - message = f"{filename} is not a file." - except ValueError as e: - message = str(e) - - if message: - return AbilityResult( - ability_name=self.name(), - ability_args={"filename": filename}, - success=False, - message=message, - data=None, - ) - - def __call__(self, filename: str) -> AbilityResult: - if result := self._check_preconditions(filename): - return result - - from unstructured.partition.auto import partition - - file_path = self._workspace.get_path(filename) - try: - elements = partition(str(file_path)) - # TODO: Lots of other potentially useful information is available - # in the partitioned file. Consider returning more of it. - new_knowledge = Knowledge( - content="\n\n".join([element.text for element in elements]), - content_type=ContentType.TEXT, - content_metadata={"filename": filename}, - ) - success = True - message = f"File {file_path} read successfully." - except IOError as e: - new_knowledge = None - success = False - message = str(e) - - return AbilityResult( - ability_name=self.name(), - ability_args={"filename": filename}, - success=success, - message=message, - new_knowledge=new_knowledge, - ) - - -class WriteFile(Ability): - default_configuration = AbilityConfiguration( - location=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.ability.builtins.WriteFile", - ), - packages_required=["unstructured"], - workspace_required=True, - ) - - def __init__( - self, - logger: logging.Logger, - workspace: Workspace, - ): - self._logger = logger - self._workspace = workspace - - description: ClassVar[str] = "Write text to a file." - - parameters: ClassVar[dict[str, JSONSchema]] = { - "filename": JSONSchema( - type=JSONSchema.Type.STRING, - description="The name of the file to write.", - ), - "contents": JSONSchema( - type=JSONSchema.Type.STRING, - description="The contents of the file to write.", - ), - } - - def _check_preconditions( - self, filename: str, contents: str - ) -> AbilityResult | None: - message = "" - try: - file_path = self._workspace.get_path(filename) - if file_path.exists(): - message = f"File {filename} already exists." - if len(contents): - message = f"File {filename} was not given any content." - except ValueError as e: - message = str(e) - - if message: - return AbilityResult( - ability_name=self.name(), - ability_args={"filename": filename, "contents": contents}, - success=False, - message=message, - data=None, - ) - - def __call__(self, filename: str, contents: str) -> AbilityResult: - if result := self._check_preconditions(filename, contents): - return result - - file_path = self._workspace.get_path(filename) - try: - directory = os.path.dirname(file_path) - os.makedirs(directory) - with open(filename, "w", encoding="utf-8") as f: - f.write(contents) - success = True - message = f"File {file_path} written successfully." - except IOError as e: - success = False - message = str(e) - - return AbilityResult( - ability_name=self.name(), - ability_args={"filename": filename}, - success=success, - message=message, - ) diff --git a/autogpts/autogpt/autogpt/core/ability/builtins/query_language_model.py b/autogpts/autogpt/autogpt/core/ability/builtins/query_language_model.py deleted file mode 100644 index f694651dba..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/builtins/query_language_model.py +++ /dev/null @@ -1,67 +0,0 @@ -import logging -from typing import ClassVar - -from forge.llm.providers import ( - ChatMessage, - ChatModelProvider, - ModelProviderName, - OpenAIModelName, -) -from forge.models.json_schema import JSONSchema - -from autogpt.core.ability.base import Ability, AbilityConfiguration -from autogpt.core.ability.schema import AbilityResult -from autogpt.core.planning.simple import LanguageModelConfiguration -from autogpt.core.plugin.simple import PluginLocation, PluginStorageFormat - - -class QueryLanguageModel(Ability): - default_configuration = AbilityConfiguration( - location=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.ability.builtins.QueryLanguageModel", - ), - language_model_required=LanguageModelConfiguration( - model_name=OpenAIModelName.GPT3, - provider_name=ModelProviderName.OPENAI, - temperature=0.9, - ), - ) - - def __init__( - self, - logger: logging.Logger, - configuration: AbilityConfiguration, - language_model_provider: ChatModelProvider, - ): - self._logger = logger - self._configuration = configuration - self._language_model_provider = language_model_provider - - description: ClassVar[str] = ( - "Query a language model." - " A query should be a question and any relevant context." - ) - - parameters: ClassVar[dict[str, JSONSchema]] = { - "query": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "A query for a language model. " - "A query should contain a question and any relevant context." - ), - ) - } - - async def __call__(self, query: str) -> AbilityResult: - model_response = await self._language_model_provider.create_chat_completion( - model_prompt=[ChatMessage.user(query)], - functions=[], - model_name=self._configuration.language_model_required.model_name, - ) - return AbilityResult( - ability_name=self.name(), - ability_args={"query": query}, - success=True, - message=model_response.response.content or "", - ) diff --git a/autogpts/autogpt/autogpt/core/ability/schema.py b/autogpts/autogpt/autogpt/core/ability/schema.py deleted file mode 100644 index 3d20a7b929..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/schema.py +++ /dev/null @@ -1,30 +0,0 @@ -import enum -from typing import Any - -from pydantic import BaseModel - - -class ContentType(str, enum.Enum): - # TBD what these actually are. - TEXT = "text" - CODE = "code" - - -class Knowledge(BaseModel): - content: str - content_type: ContentType - content_metadata: dict[str, Any] - - -class AbilityResult(BaseModel): - """The AbilityResult is a standard response struct for an ability.""" - - ability_name: str - ability_args: dict[str, str] - success: bool - message: str - new_knowledge: Knowledge = None - - def summary(self) -> str: - kwargs = ", ".join(f"{k}={v}" for k, v in self.ability_args.items()) - return f"{self.ability_name}({kwargs}): {self.message}" diff --git a/autogpts/autogpt/autogpt/core/ability/simple.py b/autogpts/autogpt/autogpt/core/ability/simple.py deleted file mode 100644 index fad09ca69d..0000000000 --- a/autogpts/autogpt/autogpt/core/ability/simple.py +++ /dev/null @@ -1,98 +0,0 @@ -import logging - -from forge.llm.providers import ( - ChatModelProvider, - CompletionModelFunction, - ModelProviderName, -) -from forge.models.config import Configurable, SystemConfiguration, SystemSettings - -from autogpt.core.ability.base import Ability, AbilityConfiguration, AbilityRegistry -from autogpt.core.ability.builtins import BUILTIN_ABILITIES -from autogpt.core.ability.schema import AbilityResult -from autogpt.core.memory.base import Memory -from autogpt.core.plugin.simple import SimplePluginService -from autogpt.core.workspace.base import Workspace - - -class AbilityRegistryConfiguration(SystemConfiguration): - """Configuration for the AbilityRegistry subsystem.""" - - abilities: dict[str, AbilityConfiguration] - - -class AbilityRegistrySettings(SystemSettings): - configuration: AbilityRegistryConfiguration - - -class SimpleAbilityRegistry(AbilityRegistry, Configurable): - default_settings = AbilityRegistrySettings( - name="simple_ability_registry", - description="A simple ability registry.", - configuration=AbilityRegistryConfiguration( - abilities={ - ability_name: ability.default_configuration - for ability_name, ability in BUILTIN_ABILITIES.items() - }, - ), - ) - - def __init__( - self, - settings: AbilityRegistrySettings, - logger: logging.Logger, - memory: Memory, - workspace: Workspace, - model_providers: dict[ModelProviderName, ChatModelProvider], - ): - self._configuration = settings.configuration - self._logger = logger - self._memory = memory - self._workspace = workspace - self._model_providers = model_providers - self._abilities: list[Ability] = [] - for ( - ability_name, - ability_configuration, - ) in self._configuration.abilities.items(): - self.register_ability(ability_name, ability_configuration) - - def register_ability( - self, ability_name: str, ability_configuration: AbilityConfiguration - ) -> None: - ability_class = SimplePluginService.get_plugin(ability_configuration.location) - ability_args = { - "logger": self._logger.getChild(ability_name), - "configuration": ability_configuration, - } - if ability_configuration.packages_required: - # TODO: Check packages are installed and maybe install them. - pass - if ability_configuration.memory_provider_required: - ability_args["memory"] = self._memory - if ability_configuration.workspace_required: - ability_args["workspace"] = self._workspace - if ability_configuration.language_model_required: - ability_args["language_model_provider"] = self._model_providers[ - ability_configuration.language_model_required.provider_name - ] - ability = ability_class(**ability_args) - self._abilities.append(ability) - - def list_abilities(self) -> list[str]: - return [ - f"{ability.name()}: {ability.description}" for ability in self._abilities - ] - - def dump_abilities(self) -> list[CompletionModelFunction]: - return [ability.spec for ability in self._abilities] - - def get_ability(self, ability_name: str) -> Ability: - for ability in self._abilities: - if ability.name() == ability_name: - return ability - raise ValueError(f"Ability '{ability_name}' not found.") - - async def perform(self, ability_name: str, **kwargs) -> AbilityResult: - ability = self.get_ability(ability_name) - return await ability(**kwargs) diff --git a/autogpts/autogpt/autogpt/core/agent/__init__.py b/autogpts/autogpt/autogpt/core/agent/__init__.py deleted file mode 100644 index f4e7a5a733..0000000000 --- a/autogpts/autogpt/autogpt/core/agent/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""The Agent is an autonomouos entity guided by a LLM provider.""" -from autogpt.core.agent.base import Agent -from autogpt.core.agent.simple import AgentSettings, SimpleAgent - -__all__ = [ - "Agent", - "AgentSettings", - "SimpleAgent", -] diff --git a/autogpts/autogpt/autogpt/core/agent/base.py b/autogpts/autogpt/autogpt/core/agent/base.py deleted file mode 100644 index c574dcea28..0000000000 --- a/autogpts/autogpt/autogpt/core/agent/base.py +++ /dev/null @@ -1,26 +0,0 @@ -import abc -import logging -from pathlib import Path - - -class Agent(abc.ABC): - @abc.abstractmethod - def __init__(self, *args, **kwargs): - ... - - @classmethod - @abc.abstractmethod - def from_workspace( - cls, - workspace_path: Path, - logger: logging.Logger, - ) -> "Agent": - ... - - @abc.abstractmethod - async def determine_next_ability(self, *args, **kwargs): - ... - - @abc.abstractmethod - def __repr__(self): - ... diff --git a/autogpts/autogpt/autogpt/core/agent/simple.py b/autogpts/autogpt/autogpt/core/agent/simple.py deleted file mode 100644 index 4b2783182f..0000000000 --- a/autogpts/autogpt/autogpt/core/agent/simple.py +++ /dev/null @@ -1,398 +0,0 @@ -import logging -from datetime import datetime -from pathlib import Path -from typing import Any - -from forge.llm.providers import CompletionModelFunction, OpenAIProvider, OpenAISettings -from forge.models.config import Configurable, SystemConfiguration, SystemSettings -from pydantic import BaseModel - -from autogpt.core.ability import ( - AbilityRegistrySettings, - AbilityResult, - SimpleAbilityRegistry, -) -from autogpt.core.agent.base import Agent -from autogpt.core.memory import MemorySettings, SimpleMemory -from autogpt.core.planning import PlannerSettings, SimplePlanner, Task, TaskStatus -from autogpt.core.plugin.simple import ( - PluginLocation, - PluginStorageFormat, - SimplePluginService, -) -from autogpt.core.workspace.simple import SimpleWorkspace, WorkspaceSettings - - -class AgentSystems(SystemConfiguration): - ability_registry: PluginLocation - memory: PluginLocation - openai_provider: PluginLocation - planning: PluginLocation - workspace: PluginLocation - - -class AgentConfiguration(SystemConfiguration): - cycle_count: int - max_task_cycle_count: int - creation_time: str - name: str - role: str - goals: list[str] - systems: AgentSystems - - -class AgentSystemSettings(SystemSettings): - configuration: AgentConfiguration - - -class AgentSettings(BaseModel): - agent: AgentSystemSettings - ability_registry: AbilityRegistrySettings - memory: MemorySettings - openai_provider: OpenAISettings - planning: PlannerSettings - workspace: WorkspaceSettings - - def update_agent_name_and_goals(self, agent_goals: dict) -> None: - self.agent.configuration.name = agent_goals["agent_name"] - self.agent.configuration.role = agent_goals["agent_role"] - self.agent.configuration.goals = agent_goals["agent_goals"] - - -class SimpleAgent(Agent, Configurable): - default_settings = AgentSystemSettings( - name="simple_agent", - description="A simple agent.", - configuration=AgentConfiguration( - name="Entrepreneur-GPT", - role=( - "An AI designed to autonomously develop and run businesses with " - "the sole goal of increasing your net worth." - ), - goals=[ - "Increase net worth", - "Grow Twitter Account", - "Develop and manage multiple businesses autonomously", - ], - cycle_count=0, - max_task_cycle_count=3, - creation_time="", - systems=AgentSystems( - ability_registry=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.ability.SimpleAbilityRegistry", - ), - memory=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.memory.SimpleMemory", - ), - openai_provider=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route=("forge.llm.model_providers.OpenAIProvider"), - ), - planning=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.planning.SimplePlanner", - ), - workspace=PluginLocation( - storage_format=PluginStorageFormat.INSTALLED_PACKAGE, - storage_route="autogpt.core.workspace.SimpleWorkspace", - ), - ), - ), - ) - - def __init__( - self, - settings: AgentSystemSettings, - logger: logging.Logger, - ability_registry: SimpleAbilityRegistry, - memory: SimpleMemory, - openai_provider: OpenAIProvider, - planning: SimplePlanner, - workspace: SimpleWorkspace, - ): - self._configuration = settings.configuration - self._logger = logger - self._ability_registry = ability_registry - self._memory = memory - # FIXME: Need some work to make this work as a dict of providers - # Getting the construction of the config to work is a bit tricky - self._openai_provider = openai_provider - self._planning = planning - self._workspace = workspace - self._task_queue = [] - self._completed_tasks = [] - self._current_task = None - self._next_ability = None - - @classmethod - def from_workspace( - cls, - workspace_path: Path, - logger: logging.Logger, - ) -> "SimpleAgent": - agent_settings = SimpleWorkspace.load_agent_settings(workspace_path) - agent_args = {} - - agent_args["settings"] = agent_settings.agent - agent_args["logger"] = logger - agent_args["workspace"] = cls._get_system_instance( - "workspace", - agent_settings, - logger, - ) - agent_args["openai_provider"] = cls._get_system_instance( - "openai_provider", - agent_settings, - logger, - ) - agent_args["planning"] = cls._get_system_instance( - "planning", - agent_settings, - logger, - model_providers={"openai": agent_args["openai_provider"]}, - ) - agent_args["memory"] = cls._get_system_instance( - "memory", - agent_settings, - logger, - workspace=agent_args["workspace"], - ) - - agent_args["ability_registry"] = cls._get_system_instance( - "ability_registry", - agent_settings, - logger, - workspace=agent_args["workspace"], - memory=agent_args["memory"], - model_providers={"openai": agent_args["openai_provider"]}, - ) - - return cls(**agent_args) - - async def build_initial_plan(self) -> dict: - plan = await self._planning.make_initial_plan( - agent_name=self._configuration.name, - agent_role=self._configuration.role, - agent_goals=self._configuration.goals, - abilities=self._ability_registry.list_abilities(), - ) - tasks = [Task.parse_obj(task) for task in plan.parsed_result["task_list"]] - - # TODO: Should probably do a step to evaluate the quality of the generated tasks - # and ensure that they have actionable ready and acceptance criteria - - self._task_queue.extend(tasks) - self._task_queue.sort(key=lambda t: t.priority, reverse=True) - self._task_queue[-1].context.status = TaskStatus.READY - return plan.parsed_result - - async def determine_next_ability(self, *args, **kwargs): - if not self._task_queue: - return {"response": "I don't have any tasks to work on right now."} - - self._configuration.cycle_count += 1 - task = self._task_queue.pop() - self._logger.info(f"Working on task: {task}") - - task = await self._evaluate_task_and_add_context(task) - next_ability = await self._choose_next_ability( - task, - self._ability_registry.dump_abilities(), - ) - self._current_task = task - self._next_ability = next_ability.parsed_result - return self._current_task, self._next_ability - - async def execute_next_ability(self, user_input: str, *args, **kwargs): - if user_input == "y": - ability = self._ability_registry.get_ability( - self._next_ability["next_ability"] - ) - ability_response = await ability(**self._next_ability["ability_arguments"]) - await self._update_tasks_and_memory(ability_response) - if self._current_task.context.status == TaskStatus.DONE: - self._completed_tasks.append(self._current_task) - else: - self._task_queue.append(self._current_task) - self._current_task = None - self._next_ability = None - - return ability_response.dict() - else: - raise NotImplementedError - - async def _evaluate_task_and_add_context(self, task: Task) -> Task: - """Evaluate the task and add context to it.""" - if task.context.status == TaskStatus.IN_PROGRESS: - # Nothing to do here - return task - else: - self._logger.debug(f"Evaluating task {task} and adding relevant context.") - # TODO: Look up relevant memories (need working memory system) - # TODO: Eval whether there is enough information to start the task (w/ LLM). - task.context.enough_info = True - task.context.status = TaskStatus.IN_PROGRESS - return task - - async def _choose_next_ability( - self, - task: Task, - ability_specs: list[CompletionModelFunction], - ): - """Choose the next ability to use for the task.""" - self._logger.debug(f"Choosing next ability for task {task}.") - if task.context.cycle_count > self._configuration.max_task_cycle_count: - # Don't hit the LLM, just set the next action as "breakdown_task" - # with an appropriate reason - raise NotImplementedError - elif not task.context.enough_info: - # Don't ask the LLM, just set the next action as "breakdown_task" - # with an appropriate reason - raise NotImplementedError - else: - next_ability = await self._planning.determine_next_ability( - task, ability_specs - ) - return next_ability - - async def _update_tasks_and_memory(self, ability_result: AbilityResult): - self._current_task.context.cycle_count += 1 - self._current_task.context.prior_actions.append(ability_result) - # TODO: Summarize new knowledge - # TODO: store knowledge and summaries in memory and in relevant tasks - # TODO: evaluate whether the task is complete - - def __repr__(self): - return "SimpleAgent()" - - ################################################################ - # Factory interface for agent bootstrapping and initialization # - ################################################################ - - @classmethod - def build_user_configuration(cls) -> dict[str, Any]: - """Build the user's configuration.""" - configuration_dict = { - "agent": cls.get_user_config(), - } - - system_locations = configuration_dict["agent"]["configuration"]["systems"] - for system_name, system_location in system_locations.items(): - system_class = SimplePluginService.get_plugin(system_location) - configuration_dict[system_name] = system_class.get_user_config() - configuration_dict = _prune_empty_dicts(configuration_dict) - return configuration_dict - - @classmethod - def compile_settings( - cls, logger: logging.Logger, user_configuration: dict - ) -> AgentSettings: - """Compile the user's configuration with the defaults.""" - logger.debug("Processing agent system configuration.") - configuration_dict = { - "agent": cls.build_agent_configuration( - user_configuration.get("agent", {}) - ).dict(), - } - - system_locations = configuration_dict["agent"]["configuration"]["systems"] - - # Build up default configuration - for system_name, system_location in system_locations.items(): - logger.debug(f"Compiling configuration for system {system_name}") - system_class = SimplePluginService.get_plugin(system_location) - configuration_dict[system_name] = system_class.build_agent_configuration( - user_configuration.get(system_name, {}) - ).dict() - - return AgentSettings.parse_obj(configuration_dict) - - @classmethod - async def determine_agent_name_and_goals( - cls, - user_objective: str, - agent_settings: AgentSettings, - logger: logging.Logger, - ) -> dict: - logger.debug("Loading OpenAI provider.") - provider: OpenAIProvider = cls._get_system_instance( - "openai_provider", - agent_settings, - logger=logger, - ) - logger.debug("Loading agent planner.") - agent_planner: SimplePlanner = cls._get_system_instance( - "planning", - agent_settings, - logger=logger, - model_providers={"openai": provider}, - ) - logger.debug("determining agent name and goals.") - model_response = await agent_planner.decide_name_and_goals( - user_objective, - ) - - return model_response.parsed_result - - @classmethod - def provision_agent( - cls, - agent_settings: AgentSettings, - logger: logging.Logger, - ): - agent_settings.agent.configuration.creation_time = datetime.now().strftime( - "%Y%m%d_%H%M%S" - ) - workspace: SimpleWorkspace = cls._get_system_instance( - "workspace", - agent_settings, - logger=logger, - ) - return workspace.setup_workspace(agent_settings, logger) - - @classmethod - def _get_system_instance( - cls, - system_name: str, - agent_settings: AgentSettings, - logger: logging.Logger, - *args, - **kwargs, - ): - system_locations = agent_settings.agent.configuration.systems.dict() - - system_settings = getattr(agent_settings, system_name) - system_class = SimplePluginService.get_plugin(system_locations[system_name]) - system_instance = system_class( - system_settings, - *args, - logger=logger.getChild(system_name), - **kwargs, - ) - return system_instance - - -def _prune_empty_dicts(d: dict) -> dict: - """ - Prune branches from a nested dictionary if the branch only contains empty - dictionaries at the leaves. - - Args: - d: The dictionary to prune. - - Returns: - The pruned dictionary. - """ - pruned = {} - for key, value in d.items(): - if isinstance(value, dict): - pruned_value = _prune_empty_dicts(value) - if ( - pruned_value - ): # if the pruned dictionary is not empty, add it to the result - pruned[key] = pruned_value - else: - pruned[key] = value - return pruned diff --git a/autogpts/autogpt/autogpt/core/memory/__init__.py b/autogpts/autogpt/autogpt/core/memory/__init__.py deleted file mode 100644 index f77b61039b..0000000000 --- a/autogpts/autogpt/autogpt/core/memory/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""The memory subsystem manages the Agent's long-term memory.""" -from autogpt.core.memory.base import Memory -from autogpt.core.memory.simple import MemorySettings, SimpleMemory - -__all__ = [ - "Memory", - "MemorySettings", - "SimpleMemory", -] diff --git a/autogpts/autogpt/autogpt/core/memory/base.py b/autogpts/autogpt/autogpt/core/memory/base.py deleted file mode 100644 index 74a4284061..0000000000 --- a/autogpts/autogpt/autogpt/core/memory/base.py +++ /dev/null @@ -1,13 +0,0 @@ -import abc - - -class Memory(abc.ABC): - pass - - -class MemoryItem(abc.ABC): - pass - - -class MessageHistory(abc.ABC): - pass diff --git a/autogpts/autogpt/autogpt/core/memory/simple.py b/autogpts/autogpt/autogpt/core/memory/simple.py deleted file mode 100644 index 9a062d704b..0000000000 --- a/autogpts/autogpt/autogpt/core/memory/simple.py +++ /dev/null @@ -1,48 +0,0 @@ -import json -import logging - -from forge.models.config import Configurable, SystemConfiguration, SystemSettings - -from autogpt.core.memory.base import Memory -from autogpt.core.workspace import Workspace - - -class MemoryConfiguration(SystemConfiguration): - pass - - -class MemorySettings(SystemSettings): - configuration: MemoryConfiguration - - -class MessageHistory: - def __init__(self, previous_message_history: list[str]): - self._message_history = previous_message_history - - -class SimpleMemory(Memory, Configurable): - default_settings = MemorySettings( - name="simple_memory", - description="A simple memory.", - configuration=MemoryConfiguration(), - ) - - def __init__( - self, - settings: MemorySettings, - logger: logging.Logger, - workspace: Workspace, - ): - self._configuration = settings.configuration - self._logger = logger - self._message_history = self._load_message_history(workspace) - - @staticmethod - def _load_message_history(workspace: Workspace): - message_history_path = workspace.get_path("message_history.json") - if message_history_path.exists(): - with message_history_path.open("r") as f: - message_history = json.load(f) - else: - message_history = [] - return MessageHistory(message_history) diff --git a/autogpts/autogpt/autogpt/core/planning/__init__.py b/autogpts/autogpt/autogpt/core/planning/__init__.py deleted file mode 100644 index 99ab573f86..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""The planning system organizes the Agent's activities.""" -from autogpt.core.planning.schema import Task, TaskStatus, TaskType -from autogpt.core.planning.simple import PlannerSettings, SimplePlanner - -__all__ = [ - "PlannerSettings", - "SimplePlanner", - "Task", - "TaskStatus", - "TaskType", -] diff --git a/autogpts/autogpt/autogpt/core/planning/base.py b/autogpts/autogpt/autogpt/core/planning/base.py deleted file mode 100644 index 7993c490be..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/base.py +++ /dev/null @@ -1,54 +0,0 @@ -# class Planner(abc.ABC): -# """ -# Manages the agent's planning and goal-setting -# by constructing language model prompts. -# """ -# -# @staticmethod -# @abc.abstractmethod -# async def decide_name_and_goals( -# user_objective: str, -# ) -> LanguageModelResponse: -# """Decide the name and goals of an Agent from a user-defined objective. -# -# Args: -# user_objective: The user-defined objective for the agent. -# -# Returns: -# The agent name and goals as a response from the language model. -# -# """ -# ... -# -# @abc.abstractmethod -# async def plan(self, context: PlanningContext) -> LanguageModelResponse: -# """Plan the next ability for the Agent. -# -# Args: -# context: A context object containing information about the agent's -# progress, result, memories, and feedback. -# -# -# Returns: -# The next ability the agent should take along with thoughts and reasoning. -# -# """ -# ... -# -# @abc.abstractmethod -# def reflect( -# self, -# context: ReflectionContext, -# ) -> LanguageModelResponse: -# """Reflect on a planned ability and provide self-criticism. -# -# -# Args: -# context: A context object containing information about the agent's -# reasoning, plan, thoughts, and criticism. -# -# Returns: -# Self-criticism about the agent's plan. -# -# """ -# ... diff --git a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/__init__.py b/autogpts/autogpt/autogpt/core/planning/prompt_strategies/__init__.py deleted file mode 100644 index 7b62279a7d..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .initial_plan import InitialPlan, InitialPlanConfiguration -from .name_and_goals import NameAndGoals, NameAndGoalsConfiguration -from .next_ability import NextAbility, NextAbilityConfiguration - -__all__ = [ - "InitialPlan", - "InitialPlanConfiguration", - "NameAndGoals", - "NameAndGoalsConfiguration", - "NextAbility", - "NextAbilityConfiguration", -] diff --git a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/initial_plan.py b/autogpts/autogpt/autogpt/core/planning/prompt_strategies/initial_plan.py deleted file mode 100644 index 83f495e407..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/initial_plan.py +++ /dev/null @@ -1,204 +0,0 @@ -import logging - -from forge.llm.prompting import ChatPrompt, LanguageModelClassification, PromptStrategy -from forge.llm.prompting.utils import to_numbered_list -from forge.llm.providers import ( - AssistantChatMessage, - ChatMessage, - CompletionModelFunction, -) -from forge.models.config import SystemConfiguration, UserConfigurable -from forge.models.json_schema import JSONSchema - -from autogpt.core.planning.schema import Task, TaskType - -logger = logging.getLogger(__name__) - - -class InitialPlanConfiguration(SystemConfiguration): - model_classification: LanguageModelClassification = UserConfigurable() - system_prompt_template: str = UserConfigurable() - system_info: list[str] = UserConfigurable() - user_prompt_template: str = UserConfigurable() - create_plan_function: dict = UserConfigurable() - - -class InitialPlan(PromptStrategy): - DEFAULT_SYSTEM_PROMPT_TEMPLATE = ( - "You are an expert project planner. " - "Your responsibility is to create work plans for autonomous agents. " - "You will be given a name, a role, set of goals for the agent to accomplish. " - "Your job is to break down those goals into a set of tasks that the agent can" - " accomplish to achieve those goals. " - "Agents are resourceful, but require clear instructions." - " Each task you create should have clearly defined `ready_criteria` that the" - " agent can check to see if the task is ready to be started." - " Each task should also have clearly defined `acceptance_criteria` that the" - " agent can check to evaluate if the task is complete. " - "You should create as many tasks as you think is necessary to accomplish" - " the goals.\n\n" - "System Info:\n{system_info}" - ) - - DEFAULT_SYSTEM_INFO = [ - "The OS you are running on is: {os_info}", - "It takes money to let you run. Your API budget is ${api_budget:.3f}", - "The current time and date is {current_time}", - ] - - DEFAULT_USER_PROMPT_TEMPLATE = ( - "You are {agent_name}, {agent_role}\n" "Your goals are:\n" "{agent_goals}" - ) - - DEFAULT_CREATE_PLAN_FUNCTION = CompletionModelFunction( - name="create_initial_agent_plan", - description=( - "Creates a set of tasks that forms the initial plan of an autonomous agent." - ), - parameters={ - "task_list": JSONSchema( - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - type=JSONSchema.Type.OBJECT, - properties={ - "objective": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "An imperative verb phrase that succinctly describes " - "the task." - ), - ), - "type": JSONSchema( - type=JSONSchema.Type.STRING, - description="A categorization for the task.", - enum=[t.value for t in TaskType], - ), - "acceptance_criteria": JSONSchema( - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "A list of measurable and testable criteria that " - "must be met for the task to be considered " - "complete." - ), - ), - ), - "priority": JSONSchema( - type=JSONSchema.Type.INTEGER, - description=( - "A number between 1 and 10 indicating the priority of " - "the task relative to other generated tasks." - ), - minimum=1, - maximum=10, - ), - "ready_criteria": JSONSchema( - type=JSONSchema.Type.ARRAY, - items=JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "A list of measurable and testable criteria that " - "must be met before the task can be started." - ), - ), - ), - }, - ), - ), - }, - ) - - default_configuration: InitialPlanConfiguration = InitialPlanConfiguration( - model_classification=LanguageModelClassification.SMART_MODEL, - system_prompt_template=DEFAULT_SYSTEM_PROMPT_TEMPLATE, - system_info=DEFAULT_SYSTEM_INFO, - user_prompt_template=DEFAULT_USER_PROMPT_TEMPLATE, - create_plan_function=DEFAULT_CREATE_PLAN_FUNCTION.schema, - ) - - def __init__( - self, - model_classification: LanguageModelClassification, - system_prompt_template: str, - system_info: list[str], - user_prompt_template: str, - create_plan_function: dict, - ): - self._model_classification = model_classification - self._system_prompt_template = system_prompt_template - self._system_info = system_info - self._user_prompt_template = user_prompt_template - self._create_plan_function = CompletionModelFunction.parse(create_plan_function) - - @property - def model_classification(self) -> LanguageModelClassification: - return self._model_classification - - def build_prompt( - self, - agent_name: str, - agent_role: str, - agent_goals: list[str], - abilities: list[str], - os_info: str, - api_budget: float, - current_time: str, - **kwargs, - ) -> ChatPrompt: - template_kwargs = { - "agent_name": agent_name, - "agent_role": agent_role, - "os_info": os_info, - "api_budget": api_budget, - "current_time": current_time, - **kwargs, - } - template_kwargs["agent_goals"] = to_numbered_list( - agent_goals, **template_kwargs - ) - template_kwargs["abilities"] = to_numbered_list(abilities, **template_kwargs) - template_kwargs["system_info"] = to_numbered_list( - self._system_info, **template_kwargs - ) - - system_prompt = ChatMessage.system( - self._system_prompt_template.format(**template_kwargs), - ) - user_prompt = ChatMessage.user( - self._user_prompt_template.format(**template_kwargs), - ) - - return ChatPrompt( - messages=[system_prompt, user_prompt], - functions=[self._create_plan_function], - # TODO: - tokens_used=0, - ) - - def parse_response_content( - self, - response_content: AssistantChatMessage, - ) -> dict: - """Parse the actual text response from the objective model. - - Args: - response_content: The raw response content from the objective model. - - Returns: - The parsed response. - """ - try: - if not response_content.tool_calls: - raise ValueError( - f"LLM did not call {self._create_plan_function.name} function; " - "plan creation failed" - ) - parsed_response: object = response_content.tool_calls[0].function.arguments - parsed_response["task_list"] = [ - Task.parse_obj(task) for task in parsed_response["task_list"] - ] - except KeyError: - logger.debug(f"Failed to parse this response content: {response_content}") - raise - return parsed_response diff --git a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/name_and_goals.py b/autogpts/autogpt/autogpt/core/planning/prompt_strategies/name_and_goals.py deleted file mode 100644 index f4864e2a8d..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/name_and_goals.py +++ /dev/null @@ -1,146 +0,0 @@ -import logging - -from forge.llm.prompting import ChatPrompt, LanguageModelClassification, PromptStrategy -from forge.llm.providers import ( - AssistantChatMessage, - ChatMessage, - CompletionModelFunction, -) -from forge.models.config import SystemConfiguration, UserConfigurable -from forge.models.json_schema import JSONSchema - -logger = logging.getLogger(__name__) - - -class NameAndGoalsConfiguration(SystemConfiguration): - model_classification: LanguageModelClassification = UserConfigurable() - system_prompt: str = UserConfigurable() - user_prompt_template: str = UserConfigurable() - create_agent_function: dict = UserConfigurable() - - -class NameAndGoals(PromptStrategy): - DEFAULT_SYSTEM_PROMPT = ( - "Your job is to respond to a user-defined task, given in triple quotes, by " - "invoking the `create_agent` function to generate an autonomous agent to " - "complete the task. " - "You should supply a role-based name for the agent, " - "an informative description for what the agent does, and " - "1 to 5 goals that are optimally aligned with the successful completion of " - "its assigned task.\n" - "\n" - "Example Input:\n" - '"""Help me with marketing my business"""\n\n' - "Example Function Call:\n" - "create_agent(name='CMOGPT', " - "description='A professional digital marketer AI that assists Solopreneurs in " - "growing their businesses by providing world-class expertise in solving " - "marketing problems for SaaS, content products, agencies, and more.', " - "goals=['Engage in effective problem-solving, prioritization, planning, and " - "supporting execution to address your marketing needs as your virtual Chief " - "Marketing Officer.', 'Provide specific, actionable, and concise advice to " - "help you make informed decisions without the use of platitudes or overly " - "wordy explanations.', 'Identify and prioritize quick wins and cost-effective " - "campaigns that maximize results with minimal time and budget investment.', " - "'Proactively take the lead in guiding you and offering suggestions when faced " - "with unclear information or uncertainty to ensure your marketing strategy " - "remains on track.'])" - ) - - DEFAULT_USER_PROMPT_TEMPLATE = '"""{user_objective}"""' - - DEFAULT_CREATE_AGENT_FUNCTION = CompletionModelFunction( - name="create_agent", - description="Create a new autonomous AI agent to complete a given task.", - parameters={ - "agent_name": JSONSchema( - type=JSONSchema.Type.STRING, - description="A short role-based name for an autonomous agent.", - ), - "agent_role": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "An informative one sentence description of what the AI agent does" - ), - ), - "agent_goals": JSONSchema( - type=JSONSchema.Type.ARRAY, - minItems=1, - maxItems=5, - items=JSONSchema( - type=JSONSchema.Type.STRING, - ), - description=( - "One to five highly effective goals that are optimally aligned " - "with the completion of a specific task. " - "The number and complexity of the goals should correspond to the " - "complexity of the agent's primary objective." - ), - ), - }, - ) - - default_configuration: NameAndGoalsConfiguration = NameAndGoalsConfiguration( - model_classification=LanguageModelClassification.SMART_MODEL, - system_prompt=DEFAULT_SYSTEM_PROMPT, - user_prompt_template=DEFAULT_USER_PROMPT_TEMPLATE, - create_agent_function=DEFAULT_CREATE_AGENT_FUNCTION.schema, - ) - - def __init__( - self, - model_classification: LanguageModelClassification, - system_prompt: str, - user_prompt_template: str, - create_agent_function: dict, - ): - self._model_classification = model_classification - self._system_prompt_message = system_prompt - self._user_prompt_template = user_prompt_template - self._create_agent_function = CompletionModelFunction.parse( - create_agent_function - ) - - @property - def model_classification(self) -> LanguageModelClassification: - return self._model_classification - - def build_prompt(self, user_objective: str = "", **kwargs) -> ChatPrompt: - system_message = ChatMessage.system(self._system_prompt_message) - user_message = ChatMessage.user( - self._user_prompt_template.format( - user_objective=user_objective, - ) - ) - prompt = ChatPrompt( - messages=[system_message, user_message], - functions=[self._create_agent_function], - # TODO - tokens_used=0, - ) - return prompt - - def parse_response_content( - self, - response_content: AssistantChatMessage, - ) -> dict: - """Parse the actual text response from the objective model. - - Args: - response_content: The raw response content from the objective model. - - Returns: - The parsed response. - - """ - try: - if not response_content.tool_calls: - raise ValueError( - f"LLM did not call {self._create_agent_function} function; " - "agent profile creation failed" - ) - parsed_response = response_content.tool_calls[0].function.arguments - except KeyError: - logger.debug(f"Failed to parse this response content: {response_content}") - raise - return parsed_response diff --git a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/next_ability.py b/autogpts/autogpt/autogpt/core/planning/prompt_strategies/next_ability.py deleted file mode 100644 index b397923d95..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/prompt_strategies/next_ability.py +++ /dev/null @@ -1,201 +0,0 @@ -import logging - -from forge.llm.prompting import ChatPrompt, LanguageModelClassification, PromptStrategy -from forge.llm.prompting.utils import to_numbered_list -from forge.llm.providers import ( - AssistantChatMessage, - ChatMessage, - CompletionModelFunction, -) -from forge.models.config import SystemConfiguration, UserConfigurable -from forge.models.json_schema import JSONSchema - -from autogpt.core.planning.schema import Task - -logger = logging.getLogger(__name__) - - -class NextAbilityConfiguration(SystemConfiguration): - model_classification: LanguageModelClassification = UserConfigurable() - system_prompt_template: str = UserConfigurable() - system_info: list[str] = UserConfigurable() - user_prompt_template: str = UserConfigurable() - additional_ability_arguments: dict = UserConfigurable() - - -class NextAbility(PromptStrategy): - DEFAULT_SYSTEM_PROMPT_TEMPLATE = "System Info:\n{system_info}" - - DEFAULT_SYSTEM_INFO = [ - "The OS you are running on is: {os_info}", - "It takes money to let you run. Your API budget is ${api_budget:.3f}", - "The current time and date is {current_time}", - ] - - DEFAULT_USER_PROMPT_TEMPLATE = ( - "Your current task is is {task_objective}.\n" - "You have taken {cycle_count} actions on this task already. " - "Here is the actions you have taken and their results:\n" - "{action_history}\n\n" - "Here is additional information that may be useful to you:\n" - "{additional_info}\n\n" - "Additionally, you should consider the following:\n" - "{user_input}\n\n" - "Your task of {task_objective} is complete when the following acceptance" - " criteria have been met:\n" - "{acceptance_criteria}\n\n" - "Please choose one of the provided functions to accomplish this task. " - "Some tasks may require multiple functions to accomplish. If that is the case," - " choose the function that you think is most appropriate for the current" - " situation given your progress so far." - ) - - DEFAULT_ADDITIONAL_ABILITY_ARGUMENTS = { - "motivation": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "Your justification for choosing choosing this function instead of a " - "different one." - ), - ), - "self_criticism": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "Thoughtful self-criticism that explains why this function may not be " - "the best choice." - ), - ), - "reasoning": JSONSchema( - type=JSONSchema.Type.STRING, - description=( - "Your reasoning for choosing this function taking into account the " - "`motivation` and weighing the `self_criticism`." - ), - ), - } - - default_configuration: NextAbilityConfiguration = NextAbilityConfiguration( - model_classification=LanguageModelClassification.SMART_MODEL, - system_prompt_template=DEFAULT_SYSTEM_PROMPT_TEMPLATE, - system_info=DEFAULT_SYSTEM_INFO, - user_prompt_template=DEFAULT_USER_PROMPT_TEMPLATE, - additional_ability_arguments={ - k: v.to_dict() for k, v in DEFAULT_ADDITIONAL_ABILITY_ARGUMENTS.items() - }, - ) - - def __init__( - self, - model_classification: LanguageModelClassification, - system_prompt_template: str, - system_info: list[str], - user_prompt_template: str, - additional_ability_arguments: dict, - ): - self._model_classification = model_classification - self._system_prompt_template = system_prompt_template - self._system_info = system_info - self._user_prompt_template = user_prompt_template - self._additional_ability_arguments = JSONSchema.parse_properties( - additional_ability_arguments - ) - for p in self._additional_ability_arguments.values(): - p.required = True - - @property - def model_classification(self) -> LanguageModelClassification: - return self._model_classification - - def build_prompt( - self, - task: Task, - ability_specs: list[CompletionModelFunction], - os_info: str, - api_budget: float, - current_time: str, - **kwargs, - ) -> ChatPrompt: - template_kwargs = { - "os_info": os_info, - "api_budget": api_budget, - "current_time": current_time, - **kwargs, - } - - for ability in ability_specs: - ability.parameters.update(self._additional_ability_arguments) - - template_kwargs["task_objective"] = task.objective - template_kwargs["cycle_count"] = task.context.cycle_count - template_kwargs["action_history"] = to_numbered_list( - [action.summary() for action in task.context.prior_actions], - no_items_response="You have not taken any actions yet.", - **template_kwargs, - ) - template_kwargs["additional_info"] = to_numbered_list( - [memory.summary() for memory in task.context.memories] - + [info for info in task.context.supplementary_info], - no_items_response=( - "There is no additional information available at this time." - ), - **template_kwargs, - ) - template_kwargs["user_input"] = to_numbered_list( - [user_input for user_input in task.context.user_input], - no_items_response="There are no additional considerations at this time.", - **template_kwargs, - ) - template_kwargs["acceptance_criteria"] = to_numbered_list( - [acceptance_criteria for acceptance_criteria in task.acceptance_criteria], - **template_kwargs, - ) - - template_kwargs["system_info"] = to_numbered_list( - self._system_info, - **template_kwargs, - ) - - system_prompt = ChatMessage.system( - self._system_prompt_template.format(**template_kwargs) - ) - user_prompt = ChatMessage.user( - self._user_prompt_template.format(**template_kwargs) - ) - - return ChatPrompt( - messages=[system_prompt, user_prompt], - functions=ability_specs, - # TODO: - tokens_used=0, - ) - - def parse_response_content( - self, - response_content: AssistantChatMessage, - ) -> dict: - """Parse the actual text response from the objective model. - - Args: - response_content: The raw response content from the objective model. - - Returns: - The parsed response. - - """ - try: - if not response_content.tool_calls: - raise ValueError("LLM did not call any function") - - function_name = response_content.tool_calls[0].function.name - function_arguments = response_content.tool_calls[0].function.arguments - parsed_response = { - "motivation": function_arguments.pop("motivation"), - "self_criticism": function_arguments.pop("self_criticism"), - "reasoning": function_arguments.pop("reasoning"), - "next_ability": function_name, - "ability_arguments": function_arguments, - } - except KeyError: - logger.debug(f"Failed to parse this response content: {response_content}") - raise - return parsed_response diff --git a/autogpts/autogpt/autogpt/core/planning/schema.py b/autogpts/autogpt/autogpt/core/planning/schema.py deleted file mode 100644 index b9ba818275..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/schema.py +++ /dev/null @@ -1,48 +0,0 @@ -import enum -from typing import Optional - -from pydantic import BaseModel, Field - -from autogpt.core.ability.schema import AbilityResult - - -class TaskType(str, enum.Enum): - RESEARCH = "research" - WRITE = "write" - EDIT = "edit" - CODE = "code" - DESIGN = "design" - TEST = "test" - PLAN = "plan" - - -class TaskStatus(str, enum.Enum): - BACKLOG = "backlog" - READY = "ready" - IN_PROGRESS = "in_progress" - DONE = "done" - - -class TaskContext(BaseModel): - cycle_count: int = 0 - status: TaskStatus = TaskStatus.BACKLOG - parent: Optional["Task"] = None - prior_actions: list[AbilityResult] = Field(default_factory=list) - memories: list = Field(default_factory=list) - user_input: list[str] = Field(default_factory=list) - supplementary_info: list[str] = Field(default_factory=list) - enough_info: bool = False - - -class Task(BaseModel): - objective: str - type: str # TaskType FIXME: gpt does not obey the enum parameter in its schema - priority: int - ready_criteria: list[str] - acceptance_criteria: list[str] - context: TaskContext = Field(default_factory=TaskContext) - - -# Need to resolve the circular dependency between Task and TaskContext -# once both models are defined. -TaskContext.update_forward_refs() diff --git a/autogpts/autogpt/autogpt/core/planning/simple.py b/autogpts/autogpt/autogpt/core/planning/simple.py deleted file mode 100644 index 6a10198210..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/simple.py +++ /dev/null @@ -1,188 +0,0 @@ -import logging -import platform -import time - -import distro -from forge.llm.prompting import PromptStrategy -from forge.llm.prompting.schema import LanguageModelClassification -from forge.llm.providers import ( - ChatModelProvider, - ChatModelResponse, - CompletionModelFunction, - ModelProviderName, - OpenAIModelName, -) -from forge.models.config import ( - Configurable, - SystemConfiguration, - SystemSettings, - UserConfigurable, -) - -from autogpt.core.planning import prompt_strategies -from autogpt.core.planning.schema import Task -from autogpt.core.runner.client_lib.logging.helpers import dump_prompt -from autogpt.core.workspace import Workspace - - -class LanguageModelConfiguration(SystemConfiguration): - """Struct for model configuration.""" - - model_name: str = UserConfigurable() - provider_name: ModelProviderName = UserConfigurable() - temperature: float = UserConfigurable() - - -class PromptStrategiesConfiguration(SystemConfiguration): - name_and_goals: prompt_strategies.NameAndGoalsConfiguration - initial_plan: prompt_strategies.InitialPlanConfiguration - next_ability: prompt_strategies.NextAbilityConfiguration - - -class PlannerConfiguration(SystemConfiguration): - """Configuration for the Planner subsystem.""" - - models: dict[LanguageModelClassification, LanguageModelConfiguration] - prompt_strategies: PromptStrategiesConfiguration - - -class PlannerSettings(SystemSettings): - """Settings for the Planner subsystem.""" - - configuration: PlannerConfiguration - - -class SimplePlanner(Configurable): - """ - Manages the agent's planning and goal-setting - by constructing language model prompts. - """ - - default_settings = PlannerSettings( - name="planner", - description=( - "Manages the agent's planning and goal-setting " - "by constructing language model prompts." - ), - configuration=PlannerConfiguration( - models={ - LanguageModelClassification.FAST_MODEL: LanguageModelConfiguration( - model_name=OpenAIModelName.GPT3, - provider_name=ModelProviderName.OPENAI, - temperature=0.9, - ), - LanguageModelClassification.SMART_MODEL: LanguageModelConfiguration( - model_name=OpenAIModelName.GPT4, - provider_name=ModelProviderName.OPENAI, - temperature=0.9, - ), - }, - prompt_strategies=PromptStrategiesConfiguration( - name_and_goals=prompt_strategies.NameAndGoals.default_configuration, - initial_plan=prompt_strategies.InitialPlan.default_configuration, - next_ability=prompt_strategies.NextAbility.default_configuration, - ), - ), - ) - - def __init__( - self, - settings: PlannerSettings, - logger: logging.Logger, - model_providers: dict[ModelProviderName, ChatModelProvider], - workspace: Workspace = None, # Workspace is not available during bootstrapping. - ) -> None: - self._configuration = settings.configuration - self._logger = logger - self._workspace = workspace - - self._providers: dict[LanguageModelClassification, ChatModelProvider] = {} - for model, model_config in self._configuration.models.items(): - self._providers[model] = model_providers[model_config.provider_name] - - self._prompt_strategies = { - "name_and_goals": prompt_strategies.NameAndGoals( - **self._configuration.prompt_strategies.name_and_goals.dict() - ), - "initial_plan": prompt_strategies.InitialPlan( - **self._configuration.prompt_strategies.initial_plan.dict() - ), - "next_ability": prompt_strategies.NextAbility( - **self._configuration.prompt_strategies.next_ability.dict() - ), - } - - async def decide_name_and_goals(self, user_objective: str) -> ChatModelResponse: - return await self.chat_with_model( - self._prompt_strategies["name_and_goals"], - user_objective=user_objective, - ) - - async def make_initial_plan( - self, - agent_name: str, - agent_role: str, - agent_goals: list[str], - abilities: list[str], - ) -> ChatModelResponse: - return await self.chat_with_model( - self._prompt_strategies["initial_plan"], - agent_name=agent_name, - agent_role=agent_role, - agent_goals=agent_goals, - abilities=abilities, - ) - - async def determine_next_ability( - self, - task: Task, - ability_specs: list[CompletionModelFunction], - ): - return await self.chat_with_model( - self._prompt_strategies["next_ability"], - task=task, - ability_specs=ability_specs, - ) - - async def chat_with_model( - self, - prompt_strategy: PromptStrategy, - **kwargs, - ) -> ChatModelResponse: - model_classification = prompt_strategy.model_classification - model_configuration = self._configuration.models[model_classification].dict() - self._logger.debug(f"Using model configuration: {model_configuration}") - del model_configuration["provider_name"] - provider = self._providers[model_classification] - - template_kwargs = self._make_template_kwargs_for_strategy(prompt_strategy) - template_kwargs.update(kwargs) - prompt = prompt_strategy.build_prompt(**template_kwargs) - - self._logger.debug(f"Using prompt:\n{dump_prompt(prompt)}\n") - response = await provider.create_chat_completion( - model_prompt=prompt.messages, - functions=prompt.functions, - **model_configuration, - completion_parser=prompt_strategy.parse_response_content, - ) - return response - - def _make_template_kwargs_for_strategy(self, strategy: PromptStrategy): - provider = self._providers[strategy.model_classification] - template_kwargs = { - "os_info": get_os_info(), - "api_budget": provider.get_remaining_budget(), - "current_time": time.strftime("%c"), - } - return template_kwargs - - -def get_os_info() -> str: - os_name = platform.system() - os_info = ( - platform.platform(terse=True) - if os_name != "Linux" - else distro.name(pretty=True) - ) - return os_info diff --git a/autogpts/autogpt/autogpt/core/planning/templates.py b/autogpts/autogpt/autogpt/core/planning/templates.py deleted file mode 100644 index 6464c8b8ad..0000000000 --- a/autogpts/autogpt/autogpt/core/planning/templates.py +++ /dev/null @@ -1,84 +0,0 @@ -# Rules of thumb: -# - Templates don't add new lines at the end of the string. This is the -# responsibility of the or a consuming template. - -#################### -# Planner defaults # -#################### - - -USER_OBJECTIVE = ( - "Write a wikipedia style article about the project: " - "https://github.com/significant-gravitas/AutoGPT" -) - - -# Plan Prompt -# ----------- - - -PLAN_PROMPT_CONSTRAINTS = ( - "~4000 word limit for short term memory. Your short term memory is short, so " - "immediately save important information to files.", - "If you are unsure how you previously did something or want to recall past " - "events, thinking about similar events will help you remember.", - "No user assistance", - "Exclusively use the commands listed below e.g. command_name", -) - -PLAN_PROMPT_RESOURCES = ( - "Internet access for searches and information gathering.", - "Long-term memory management.", - "File output.", -) - -PLAN_PROMPT_PERFORMANCE_EVALUATIONS = ( - "Continuously review and analyze your actions to ensure you are performing to" - " the best of your abilities.", - "Constructively self-criticize your big-picture behavior constantly.", - "Reflect on past decisions and strategies to refine your approach.", - "Every command has a cost, so be smart and efficient. Aim to complete tasks in" - " the least number of steps.", - "Write all code to a file", -) - - -PLAN_PROMPT_RESPONSE_DICT = { - "thoughts": { - "text": "thought", - "reasoning": "reasoning", - "plan": "- short bulleted\n- list that conveys\n- long-term plan", - "criticism": "constructive self-criticism", - "speak": "thoughts summary to say to user", - }, - "command": {"name": "command name", "args": {"arg name": "value"}}, -} - -PLAN_PROMPT_RESPONSE_FORMAT = ( - "You should only respond in JSON format as described below\n" - "Response Format:\n" - "{response_json_structure}\n" - "Ensure the response can be parsed by Python json.loads" -) - -PLAN_TRIGGERING_PROMPT = ( - "Determine which next command to use, and respond using the format specified above:" -) - -PLAN_PROMPT_MAIN = ( - "{header}\n\n" - "GOALS:\n\n{goals}\n\n" - "Info:\n{info}\n\n" - "Constraints:\n{constraints}\n\n" - "Commands:\n{commands}\n\n" - "Resources:\n{resources}\n\n" - "Performance Evaluations:\n{performance_evaluations}\n\n" - "You should only respond in JSON format as described below\n" - "Response Format:\n{response_json_structure}\n" - "Ensure the response can be parsed by Python json.loads" -) - - -########################### -# Parameterized templates # -########################### diff --git a/autogpts/autogpt/autogpt/core/plugin/__init__.py b/autogpts/autogpt/autogpt/core/plugin/__init__.py deleted file mode 100644 index b850114b39..0000000000 --- a/autogpts/autogpt/autogpt/core/plugin/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""The plugin system allows the Agent to be extended with new functionality.""" -from autogpt.core.plugin.base import PluginService - -__all__ = [ - "PluginService", -] diff --git a/autogpts/autogpt/autogpt/core/plugin/base.py b/autogpts/autogpt/autogpt/core/plugin/base.py deleted file mode 100644 index 8eb1dce659..0000000000 --- a/autogpts/autogpt/autogpt/core/plugin/base.py +++ /dev/null @@ -1,159 +0,0 @@ -import abc -import enum -from typing import TYPE_CHECKING, Type - -from forge.models.config import SystemConfiguration, UserConfigurable -from pydantic import BaseModel - -if TYPE_CHECKING: - from forge.llm.providers import ChatModelProvider, EmbeddingModelProvider - - from autogpt.core.ability import Ability, AbilityRegistry - from autogpt.core.memory import Memory - - # Expand to other types as needed - PluginType = ( - Type[Ability] # Swappable now - | Type[AbilityRegistry] # Swappable maybe never - | Type[ChatModelProvider] # Swappable soon - | Type[EmbeddingModelProvider] # Swappable soon - | Type[Memory] # Swappable now - # | Type[Planner] # Swappable soon - ) - - -class PluginStorageFormat(str, enum.Enum): - """Supported plugin storage formats. - - Plugins can be stored at one of these supported locations. - - """ - - INSTALLED_PACKAGE = "installed_package" # Required now, loads system defaults - WORKSPACE = "workspace" # Required now - - # Soon (requires some tooling we don't have yet). - # OPENAPI_URL = "open_api_url" - - # OTHER_FILE_PATH = "other_file_path" # Maybe later (maybe now) - # GIT = "git" # Maybe later (or soon) - # PYPI = "pypi" # Maybe later - - # Long term solution, requires design - # AUTOGPT_PLUGIN_SERVICE = "autogpt_plugin_service" - - # Feature for later maybe, automatically find plugin. - # AUTO = "auto" - - -# Installed package example -# PluginLocation( -# storage_format='installed_package', -# storage_route='autogpt_plugins.twitter.SendTwitterMessage' -# ) -# Workspace example -# PluginLocation( -# storage_format='workspace', -# storage_route='relative/path/to/plugin.pkl' -# OR -# storage_route='relative/path/to/plugin.py' -# ) -# Git -# PluginLocation( -# storage_format='git', -# Exact format TBD. -# storage_route='https://github.com/gravelBridge/AutoGPT-WolframAlpha/blob/main/autogpt-wolframalpha/wolfram_alpha.py' -# ) -# PyPI -# PluginLocation( -# storage_format='pypi', -# storage_route='package_name' -# ) - - -# PluginLocation( -# storage_format='installed_package', -# storage_route='autogpt_plugins.twitter.SendTwitterMessage' -# ) - - -# A plugin storage route. -# -# This is a string that specifies where to load a plugin from -# (e.g. an import path or file path). -PluginStorageRoute = str - - -class PluginLocation(SystemConfiguration): - """A plugin location. - - This is a combination of a plugin storage format and a plugin storage route. - It is used by the PluginService to load plugins. - - """ - - storage_format: PluginStorageFormat = UserConfigurable() - storage_route: PluginStorageRoute = UserConfigurable() - - -class PluginMetadata(BaseModel): - """Metadata about a plugin.""" - - name: str - description: str - location: PluginLocation - - -class PluginService(abc.ABC): - """Base class for plugin service. - - The plugin service should be stateless. This defines the interface for - loading plugins from various storage formats. - - """ - - @staticmethod - @abc.abstractmethod - def get_plugin(plugin_location: PluginLocation) -> "PluginType": - """Get a plugin from a plugin location.""" - ... - - #################################### - # Low-level storage format loaders # - #################################### - @staticmethod - @abc.abstractmethod - def load_from_file_path(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from a file path.""" - - ... - - @staticmethod - @abc.abstractmethod - def load_from_import_path(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from an import path.""" - ... - - @staticmethod - @abc.abstractmethod - def resolve_name_to_path( - plugin_route: PluginStorageRoute, path_type: str - ) -> PluginStorageRoute: - """Resolve a plugin name to a plugin path.""" - ... - - ##################################### - # High-level storage format loaders # - ##################################### - - @staticmethod - @abc.abstractmethod - def load_from_workspace(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from the workspace.""" - ... - - @staticmethod - @abc.abstractmethod - def load_from_installed_package(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from an installed package.""" - ... diff --git a/autogpts/autogpt/autogpt/core/plugin/simple.py b/autogpts/autogpt/autogpt/core/plugin/simple.py deleted file mode 100644 index 7f0e60608d..0000000000 --- a/autogpts/autogpt/autogpt/core/plugin/simple.py +++ /dev/null @@ -1,75 +0,0 @@ -from importlib import import_module -from typing import TYPE_CHECKING - -from autogpt.core.plugin.base import ( - PluginLocation, - PluginService, - PluginStorageFormat, - PluginStorageRoute, -) - -if TYPE_CHECKING: - from autogpt.core.plugin.base import PluginType - - -class SimplePluginService(PluginService): - @staticmethod - def get_plugin(plugin_location: dict | PluginLocation) -> "PluginType": - """Get a plugin from a plugin location.""" - if isinstance(plugin_location, dict): - plugin_location = PluginLocation.parse_obj(plugin_location) - if plugin_location.storage_format == PluginStorageFormat.WORKSPACE: - return SimplePluginService.load_from_workspace( - plugin_location.storage_route - ) - elif plugin_location.storage_format == PluginStorageFormat.INSTALLED_PACKAGE: - return SimplePluginService.load_from_installed_package( - plugin_location.storage_route - ) - else: - raise NotImplementedError( - "Plugin storage format %s is not implemented." - % plugin_location.storage_format - ) - - #################################### - # Low-level storage format loaders # - #################################### - @staticmethod - def load_from_file_path(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from a file path.""" - # TODO: Define an on disk storage format and implement this. - # Can pull from existing zip file loading implementation - raise NotImplementedError("Loading from file path is not implemented.") - - @staticmethod - def load_from_import_path(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from an import path.""" - module_path, _, class_name = plugin_route.rpartition(".") - return getattr(import_module(module_path), class_name) - - @staticmethod - def resolve_name_to_path( - plugin_route: PluginStorageRoute, path_type: str - ) -> PluginStorageRoute: - """Resolve a plugin name to a plugin path.""" - # TODO: Implement a discovery system for finding plugins by name from known - # storage locations. E.g. if we know that path_type is a file path, we can - # search the workspace for it. If it's an import path, we can check the core - # system and the auto_gpt_plugins package. - raise NotImplementedError("Resolving plugin name to path is not implemented.") - - ##################################### - # High-level storage format loaders # - ##################################### - - @staticmethod - def load_from_workspace(plugin_route: PluginStorageRoute) -> "PluginType": - """Load a plugin from the workspace.""" - plugin = SimplePluginService.load_from_file_path(plugin_route) - return plugin - - @staticmethod - def load_from_installed_package(plugin_route: PluginStorageRoute) -> "PluginType": - plugin = SimplePluginService.load_from_import_path(plugin_route) - return plugin diff --git a/autogpts/autogpt/autogpt/core/poetry.lock b/autogpts/autogpt/autogpt/core/poetry.lock deleted file mode 100644 index 9b3a0ccd17..0000000000 --- a/autogpts/autogpt/autogpt/core/poetry.lock +++ /dev/null @@ -1,1345 +0,0 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. - -[[package]] -name = "agent-protocol" -version = "0.3.0" -description = "API for interacting with Agent" -optional = false -python-versions = ">=3.7,<4.0.0" -files = [ - {file = "agent_protocol-0.3.0-py3-none-any.whl", hash = "sha256:717d0fdad2e105968120fa0a99f0b29e08890951e9cbd74740dd10abf4cfe6dc"}, - {file = "agent_protocol-0.3.0.tar.gz", hash = "sha256:6239820753246bbc69f7f531293b32c69f23284158d58873ee55fe9916cd6028"}, -] - -[package.dependencies] -aiofiles = ">=23.1.0,<24.0.0" -click = ">=8.1.6,<9.0.0" -fastapi = ">=0.100.0,<0.101.0" -hypercorn = ">=0.14.4,<0.15.0" -pydantic = ">=1.10.5,<2.0.0" -pytest = ">=7.0.0,<8.0.0" -python-multipart = ">=0.0.6,<0.0.7" -requests = ">=2.31.0,<3.0.0" - -[[package]] -name = "aiofiles" -version = "23.2.1" -description = "File support for asyncio." -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, - {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, -] - -[[package]] -name = "aiohttp" -version = "3.8.5" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.6" -files = [ - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"}, - {file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"}, - {file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"}, - {file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"}, - {file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"}, - {file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"}, - {file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"}, - {file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"}, - {file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"}, - {file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"}, - {file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"}, -] - -[package.dependencies] -aiosignal = ">=1.1.2" -async-timeout = ">=4.0.0a3,<5.0" -attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<4.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "cchardet"] - -[[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "anyio" -version = "4.0.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" -files = [ - {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, - {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.22)"] - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - -[[package]] -name = "certifi" -version = "2023.7.22" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "distro" -version = "1.8.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -files = [ - {file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"}, - {file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "fastapi" -version = "0.100.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.7" -files = [ - {file = "fastapi-0.100.1-py3-none-any.whl", hash = "sha256:ec6dd52bfc4eff3063cfcd0713b43c87640fefb2687bbbe3d8a08d94049cdf32"}, - {file = "fastapi-0.100.1.tar.gz", hash = "sha256:522700d7a469e4a973d92321ab93312448fbe20fca9c8da97effc7e7bc56df23"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<3.0.0" -starlette = ">=0.27.0,<0.28.0" -typing-extensions = ">=4.5.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "frozenlist" -version = "1.4.0" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -files = [ - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, - {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, - {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, - {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, - {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, - {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, - {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, - {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, - {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, - {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, -] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "h2" -version = "4.1.0" -description = "HTTP/2 State-Machine based protocol implementation" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, - {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, -] - -[package.dependencies] -hpack = ">=4.0,<5" -hyperframe = ">=6.0,<7" - -[[package]] -name = "hpack" -version = "4.0.0" -description = "Pure-Python HPACK header compression" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, - {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, -] - -[[package]] -name = "hypercorn" -version = "0.14.4" -description = "A ASGI Server based on Hyper libraries and inspired by Gunicorn" -optional = false -python-versions = ">=3.7" -files = [ - {file = "hypercorn-0.14.4-py3-none-any.whl", hash = "sha256:f956200dbf8677684e6e976219ffa6691d6cf795281184b41dbb0b135ab37b8d"}, - {file = "hypercorn-0.14.4.tar.gz", hash = "sha256:3fa504efc46a271640023c9b88c3184fd64993f47a282e8ae1a13ccb285c2f67"}, -] - -[package.dependencies] -h11 = "*" -h2 = ">=3.1.0" -priority = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} -wsproto = ">=0.14.0" - -[package.extras] -docs = ["pydata_sphinx_theme"] -h3 = ["aioquic (>=0.9.0,<1.0)"] -trio = ["exceptiongroup (>=1.1.0)", "trio (>=0.22.0)"] -uvloop = ["uvloop"] - -[[package]] -name = "hyperframe" -version = "6.0.1" -description = "HTTP/2 framing layer for Python" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, - {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, -] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "inflection" -version = "0.5.1" -description = "A port of Ruby on Rails inflector to Python" -optional = false -python-versions = ">=3.5" -files = [ - {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, - {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "jsonschema" -version = "4.19.1" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema-4.19.1-py3-none-any.whl", hash = "sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e"}, - {file = "jsonschema-4.19.1.tar.gz", hash = "sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jsonschema-specifications" -version = "2023.7.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1"}, - {file = "jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb"}, -] - -[package.dependencies] -referencing = ">=0.28.0" - -[[package]] -name = "multidict" -version = "6.0.4" -description = "multidict implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, -] - -[[package]] -name = "openai" -version = "0.28.0" -description = "Python client library for the OpenAI API" -optional = false -python-versions = ">=3.7.1" -files = [ - {file = "openai-0.28.0-py3-none-any.whl", hash = "sha256:d207ece78469be5648eb87b825753282225155a29d0eec6e02013ddbf8c31c0c"}, - {file = "openai-0.28.0.tar.gz", hash = "sha256:417b78c4c2864ba696aedaf1ccff77be1f04a581ab1739f0a56e0aae19e5a794"}, -] - -[package.dependencies] -aiohttp = "*" -requests = ">=2.20" -tqdm = "*" - -[package.extras] -datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-mock"] -embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] -wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "pluggy" -version = "1.3.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "priority" -version = "2.0.0" -description = "A pure-Python implementation of the HTTP/2 priority tree" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"}, - {file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"}, -] - -[[package]] -name = "pydantic" -version = "1.10.12" -description = "Data validation and settings management using python type hints" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718"}, - {file = "pydantic-1.10.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe"}, - {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b"}, - {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d"}, - {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09"}, - {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed"}, - {file = "pydantic-1.10.12-cp310-cp310-win_amd64.whl", hash = "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a"}, - {file = "pydantic-1.10.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc"}, - {file = "pydantic-1.10.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405"}, - {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62"}, - {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494"}, - {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246"}, - {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33"}, - {file = "pydantic-1.10.12-cp311-cp311-win_amd64.whl", hash = "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f"}, - {file = "pydantic-1.10.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a"}, - {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565"}, - {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350"}, - {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303"}, - {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5"}, - {file = "pydantic-1.10.12-cp37-cp37m-win_amd64.whl", hash = "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8"}, - {file = "pydantic-1.10.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62"}, - {file = "pydantic-1.10.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb"}, - {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0"}, - {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c"}, - {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d"}, - {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33"}, - {file = "pydantic-1.10.12-cp38-cp38-win_amd64.whl", hash = "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47"}, - {file = "pydantic-1.10.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6"}, - {file = "pydantic-1.10.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523"}, - {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86"}, - {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1"}, - {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe"}, - {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb"}, - {file = "pydantic-1.10.12-cp39-cp39-win_amd64.whl", hash = "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d"}, - {file = "pydantic-1.10.12-py3-none-any.whl", hash = "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942"}, - {file = "pydantic-1.10.12.tar.gz", hash = "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pytest" -version = "7.4.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "python-multipart" -version = "0.0.6" -description = "A streaming multipart parser for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "python_multipart-0.0.6-py3-none-any.whl", hash = "sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"}, - {file = "python_multipart-0.0.6.tar.gz", hash = "sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132"}, -] - -[package.extras] -dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "referencing" -version = "0.30.2" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "referencing-0.30.2-py3-none-any.whl", hash = "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf"}, - {file = "referencing-0.30.2.tar.gz", hash = "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - -[[package]] -name = "regex" -version = "2023.8.8" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.6" -files = [ - {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, - {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"}, - {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"}, - {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"}, - {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"}, - {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"}, - {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"}, - {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"}, - {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"}, - {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"}, - {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"}, - {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"}, - {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"}, - {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"}, - {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"}, - {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"}, - {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"}, - {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"}, - {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"}, - {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"}, - {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"}, - {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"}, - {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"}, - {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"}, - {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"}, - {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"}, - {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"}, - {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"}, - {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"}, - {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"}, - {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"}, - {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"}, - {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"}, - {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"}, - {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"}, - {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"}, - {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"}, - {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"}, - {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"}, - {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"}, - {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"}, - {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"}, - {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"}, - {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"}, - {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"}, - {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"}, - {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"}, - {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"}, - {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"}, - {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"}, - {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"}, - {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"}, - {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"}, - {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"}, - {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"}, - {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"}, - {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"}, - {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"}, - {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"}, - {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"}, - {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"}, - {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"}, - {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rpds-py" -version = "0.10.3" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rpds_py-0.10.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e"}, - {file = "rpds_py-0.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff"}, - {file = "rpds_py-0.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a"}, - {file = "rpds_py-0.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33"}, - {file = "rpds_py-0.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec"}, - {file = "rpds_py-0.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e"}, - {file = "rpds_py-0.10.3-cp310-none-win32.whl", hash = "sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391"}, - {file = "rpds_py-0.10.3-cp310-none-win_amd64.whl", hash = "sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860"}, - {file = "rpds_py-0.10.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453"}, - {file = "rpds_py-0.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f"}, - {file = "rpds_py-0.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1"}, - {file = "rpds_py-0.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836"}, - {file = "rpds_py-0.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8"}, - {file = "rpds_py-0.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff"}, - {file = "rpds_py-0.10.3-cp311-none-win32.whl", hash = "sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48"}, - {file = "rpds_py-0.10.3-cp311-none-win_amd64.whl", hash = "sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c"}, - {file = "rpds_py-0.10.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b"}, - {file = "rpds_py-0.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6"}, - {file = "rpds_py-0.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a"}, - {file = "rpds_py-0.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25"}, - {file = "rpds_py-0.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2"}, - {file = "rpds_py-0.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f"}, - {file = "rpds_py-0.10.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3"}, - {file = "rpds_py-0.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a"}, - {file = "rpds_py-0.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c"}, - {file = "rpds_py-0.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf"}, - {file = "rpds_py-0.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496"}, - {file = "rpds_py-0.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b"}, - {file = "rpds_py-0.10.3-cp38-none-win32.whl", hash = "sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1"}, - {file = "rpds_py-0.10.3-cp38-none-win_amd64.whl", hash = "sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee"}, - {file = "rpds_py-0.10.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623"}, - {file = "rpds_py-0.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9"}, - {file = "rpds_py-0.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154"}, - {file = "rpds_py-0.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e"}, - {file = "rpds_py-0.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc"}, - {file = "rpds_py-0.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1"}, - {file = "rpds_py-0.10.3-cp39-none-win32.whl", hash = "sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065"}, - {file = "rpds_py-0.10.3-cp39-none-win_amd64.whl", hash = "sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557"}, - {file = "rpds_py-0.10.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac"}, - {file = "rpds_py-0.10.3-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97"}, - {file = "rpds_py-0.10.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599"}, - {file = "rpds_py-0.10.3.tar.gz", hash = "sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb"}, -] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "starlette" -version = "0.27.0" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "tiktoken" -version = "0.5.1" -description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tiktoken-0.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b0bae3fd56de1c0a5874fb6577667a3c75bf231a6cef599338820210c16e40a"}, - {file = "tiktoken-0.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e529578d017045e2f0ed12d2e00e7e99f780f477234da4aae799ec4afca89f37"}, - {file = "tiktoken-0.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edd2ffbb789712d83fee19ab009949f998a35c51ad9f9beb39109357416344ff"}, - {file = "tiktoken-0.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c73d47bdc1a3f1f66ffa019af0386c48effdc6e8797e5e76875f6388ff72e9"}, - {file = "tiktoken-0.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46b8554b9f351561b1989157c6bb54462056f3d44e43aa4e671367c5d62535fc"}, - {file = "tiktoken-0.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92ed3bbf71a175a6a4e5fbfcdb2c422bdd72d9b20407e00f435cf22a68b4ea9b"}, - {file = "tiktoken-0.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:714efb2f4a082635d9f5afe0bf7e62989b72b65ac52f004eb7ac939f506c03a4"}, - {file = "tiktoken-0.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a10488d1d1a5f9c9d2b2052fdb4cf807bba545818cb1ef724a7f5d44d9f7c3d4"}, - {file = "tiktoken-0.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8079ac065572fe0e7c696dbd63e1fdc12ce4cdca9933935d038689d4732451df"}, - {file = "tiktoken-0.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ef730db4097f5b13df8d960f7fdda2744fe21d203ea2bb80c120bb58661b155"}, - {file = "tiktoken-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:426e7def5f3f23645dada816be119fa61e587dfb4755de250e136b47a045c365"}, - {file = "tiktoken-0.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:323cec0031358bc09aa965c2c5c1f9f59baf76e5b17e62dcc06d1bb9bc3a3c7c"}, - {file = "tiktoken-0.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5abd9436f02e2c8eda5cce2ff8015ce91f33e782a7423de2a1859f772928f714"}, - {file = "tiktoken-0.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:1fe99953b63aabc0c9536fbc91c3c9000d78e4755edc28cc2e10825372046a2d"}, - {file = "tiktoken-0.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dcdc630461927718b317e6f8be7707bd0fc768cee1fdc78ddaa1e93f4dc6b2b1"}, - {file = "tiktoken-0.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1f2b3b253e22322b7f53a111e1f6d7ecfa199b4f08f3efdeb0480f4033b5cdc6"}, - {file = "tiktoken-0.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43ce0199f315776dec3ea7bf86f35df86d24b6fcde1babd3e53c38f17352442f"}, - {file = "tiktoken-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a84657c083d458593c0235926b5c993eec0b586a2508d6a2020556e5347c2f0d"}, - {file = "tiktoken-0.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c008375c0f3d97c36e81725308699116cd5804fdac0f9b7afc732056329d2790"}, - {file = "tiktoken-0.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:779c4dea5edd1d3178734d144d32231e0b814976bec1ec09636d1003ffe4725f"}, - {file = "tiktoken-0.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:b5dcfcf9bfb798e86fbce76d40a1d5d9e3f92131aecfa3d1e5c9ea1a20f1ef1a"}, - {file = "tiktoken-0.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b180a22db0bbcc447f691ffc3cf7a580e9e0587d87379e35e58b826ebf5bc7b"}, - {file = "tiktoken-0.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b756a65d98b7cf760617a6b68762a23ab8b6ef79922be5afdb00f5e8a9f4e76"}, - {file = "tiktoken-0.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba9873c253ca1f670e662192a0afcb72b41e0ba3e730f16c665099e12f4dac2d"}, - {file = "tiktoken-0.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74c90d2be0b4c1a2b3f7dde95cd976757817d4df080d6af0ee8d461568c2e2ad"}, - {file = "tiktoken-0.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:709a5220891f2b56caad8327fab86281787704931ed484d9548f65598dea9ce4"}, - {file = "tiktoken-0.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d5a187ff9c786fae6aadd49f47f019ff19e99071dc5b0fe91bfecc94d37c686"}, - {file = "tiktoken-0.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:e21840043dbe2e280e99ad41951c00eff8ee3b63daf57cd4c1508a3fd8583ea2"}, - {file = "tiktoken-0.5.1.tar.gz", hash = "sha256:27e773564232004f4f810fd1f85236673ec3a56ed7f1206fc9ed8670ebedb97a"}, -] - -[package.dependencies] -regex = ">=2022.1.18" -requests = ">=2.26.0" - -[package.extras] -blobfile = ["blobfile (>=2)"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tqdm" -version = "4.66.1" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, - {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - -[[package]] -name = "urllib3" -version = "2.0.5" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, - {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "wsproto" -version = "1.2.0" -description = "WebSockets state-machine based protocol implementation" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, - {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, -] - -[package.dependencies] -h11 = ">=0.9.0,<1" - -[[package]] -name = "yarl" -version = "1.9.2" -description = "Yet another URL library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, - {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, - {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, - {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, - {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, - {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, - {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, - {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, - {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, - {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, - {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, - {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, - {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "e5acc4decd67692ad0f08e38d380e1a474ef480449b78dd14321dccf1ad3ca5a" diff --git a/autogpts/autogpt/autogpt/core/pyproject.toml b/autogpts/autogpt/autogpt/core/pyproject.toml deleted file mode 100644 index 059a6bc760..0000000000 --- a/autogpts/autogpt/autogpt/core/pyproject.toml +++ /dev/null @@ -1,77 +0,0 @@ -[tool.poetry] -name = "agpt" -version = "1.0.0" -authors = ["Significant Gravitas "] -maintainers = ["Reinier van der Leer "] -description = "An open-source attempt at an autonomous generalist agent" -readme = "README.md" -repository = "https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/agpt" -# documentation = "https://docs.agpt.co/autogpts/agpt" # TODO -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] -packages = [{ include = "autogpt/core", from = "../.." }] - -[tool.poetry.scripts] -cli = "autogpt.core.runner.cli_app.cli:autogpt" -cli-web = "autogpt.core.runner.cli_web_app.cli:autogpt" - -[tool.poetry.dependencies] -python = "^3.10" -agent-protocol = "^0.3.0" -click = "^8.1.7" -colorama = "^0.4.6" -distro = "^1.8.0" -inflection = "^0.5.1" -jsonschema = "^4.19.1" -openai = "^0.28.0" -pydantic = "^1.10.12" -pyyaml = "^6.0.0" -tiktoken = "^0.5.1" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - - -[tool.black] -line-length = 88 -target-version = ['py310'] -include = '\.pyi?$' -packages = ["autogpt"] -extend-exclude = '.+/(dist|.venv|venv|build)/.+' - -[tool.isort] -profile = "black" -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -ensure_newline_before_comments = true -line_length = 88 -sections = [ - "FUTURE", - "STDLIB", - "THIRDPARTY", - "FIRSTPARTY", - "LOCALFOLDER" -] -skip = ''' - .tox - __pycache__ - *.pyc - .env - venv*/* - .venv/* - reports/* - dist/* - -''' - -[tool.pytest.ini_options] -markers = [ - "requires_openai_api_key", - "requires_huggingface_api_key" -] diff --git a/autogpts/autogpt/autogpt/core/runner/__init__.py b/autogpts/autogpt/autogpt/core/runner/__init__.py deleted file mode 100644 index 25c7b65088..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -This module contains the runner for the v2 agent server and client. -""" diff --git a/autogpts/autogpt/autogpt/core/runner/cli_app/cli.py b/autogpts/autogpt/autogpt/core/runner/cli_app/cli.py deleted file mode 100644 index d54acf53b2..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/cli_app/cli.py +++ /dev/null @@ -1,47 +0,0 @@ -from pathlib import Path - -import click -import yaml - -from autogpt.core.runner.cli_app.main import run_auto_gpt -from autogpt.core.runner.client_lib.shared_click_commands import ( - DEFAULT_SETTINGS_FILE, - make_settings, -) -from autogpt.core.runner.client_lib.utils import coroutine, handle_exceptions - - -@click.group() -def autogpt(): - """Temporary command group for v2 commands.""" - pass - - -autogpt.add_command(make_settings) - - -@autogpt.command() -@click.option( - "--settings-file", - type=click.Path(), - default=DEFAULT_SETTINGS_FILE, -) -@click.option( - "--pdb", - is_flag=True, - help="Drop into a debugger if an error is raised.", -) -@coroutine -async def run(settings_file: str, pdb: bool) -> None: - """Run the AutoGPT agent.""" - click.echo("Running AutoGPT agent...") - settings_file: Path = Path(settings_file) - settings = {} - if settings_file.exists(): - settings = yaml.safe_load(settings_file.read_text()) - main = handle_exceptions(run_auto_gpt, with_debugger=pdb) - await main(settings) - - -if __name__ == "__main__": - autogpt() diff --git a/autogpts/autogpt/autogpt/core/runner/cli_app/main.py b/autogpts/autogpt/autogpt/core/runner/cli_app/main.py deleted file mode 100644 index d6bb5c4f02..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/cli_app/main.py +++ /dev/null @@ -1,74 +0,0 @@ -import click - -from autogpt.core.agent import AgentSettings, SimpleAgent -from autogpt.core.runner.client_lib.logging import ( - configure_root_logger, - get_client_logger, -) -from autogpt.core.runner.client_lib.parser import ( - parse_ability_result, - parse_agent_name_and_goals, - parse_agent_plan, - parse_next_ability, -) - - -async def run_auto_gpt(user_configuration: dict): - """Run the AutoGPT CLI client.""" - - configure_root_logger() - - client_logger = get_client_logger() - client_logger.debug("Getting agent settings") - - agent_workspace = ( - user_configuration.get("workspace", {}).get("configuration", {}).get("root", "") - ) - - if not agent_workspace: # We don't have an agent yet. - ################# - # Bootstrapping # - ################# - # Step 1. Collate the user's settings with the default system settings. - agent_settings: AgentSettings = SimpleAgent.compile_settings( - client_logger, - user_configuration, - ) - - # Step 2. Get a name and goals for the agent. - # First we need to figure out what the user wants to do with the agent. - # We'll do this by asking the user for a prompt. - user_objective = click.prompt("What do you want AutoGPT to do?") - # Ask a language model to determine a name and goals for a suitable agent. - name_and_goals = await SimpleAgent.determine_agent_name_and_goals( - user_objective, - agent_settings, - client_logger, - ) - print("\n" + parse_agent_name_and_goals(name_and_goals)) - # Finally, update the agent settings with the name and goals. - agent_settings.update_agent_name_and_goals(name_and_goals) - - # Step 3. Provision the agent. - agent_workspace = SimpleAgent.provision_agent(agent_settings, client_logger) - client_logger.info("Agent is provisioned") - - # launch agent interaction loop - agent = SimpleAgent.from_workspace( - agent_workspace, - client_logger, - ) - client_logger.info("Agent is loaded") - - plan = await agent.build_initial_plan() - print(parse_agent_plan(plan)) - - while True: - current_task, next_ability = await agent.determine_next_ability(plan) - print(parse_next_ability(current_task, next_ability)) - user_input = click.prompt( - "Should the agent proceed with this ability?", - default="y", - ) - ability_result = await agent.execute_next_ability(user_input) - print(parse_ability_result(ability_result)) diff --git a/autogpts/autogpt/autogpt/core/runner/cli_web_app/cli.py b/autogpts/autogpt/autogpt/core/runner/cli_web_app/cli.py deleted file mode 100644 index e00bb33b73..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/cli_web_app/cli.py +++ /dev/null @@ -1,58 +0,0 @@ -import pathlib - -import click -import yaml -from agent_protocol import Agent as AgentProtocol - -from autogpt.core.runner.cli_web_app.server.api import task_handler -from autogpt.core.runner.client_lib.shared_click_commands import ( - DEFAULT_SETTINGS_FILE, - make_settings, -) -from autogpt.core.runner.client_lib.utils import coroutine - - -@click.group() -def autogpt(): - """Temporary command group for v2 commands.""" - pass - - -autogpt.add_command(make_settings) - - -@autogpt.command() -@click.option( - "port", - "--port", - default=8080, - help="The port of the webserver.", - type=click.INT, -) -def server(port: int) -> None: - """Run the AutoGPT runner httpserver.""" - click.echo("Running AutoGPT runner httpserver...") - AgentProtocol.handle_task(task_handler).start(port) - - -@autogpt.command() -@click.option( - "--settings-file", - type=click.Path(), - default=DEFAULT_SETTINGS_FILE, -) -@coroutine -async def client(settings_file) -> None: - """Run the AutoGPT runner client.""" - settings_file = pathlib.Path(settings_file) - settings = {} - if settings_file.exists(): - settings = yaml.safe_load(settings_file.read_text()) - - settings - # TODO: Call the API server with the settings and task, - # using the Python API client for agent protocol. - - -if __name__ == "__main__": - autogpt() diff --git a/autogpts/autogpt/autogpt/core/runner/cli_web_app/server/api.py b/autogpts/autogpt/autogpt/core/runner/cli_web_app/server/api.py deleted file mode 100644 index 76c68a7e80..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/cli_web_app/server/api.py +++ /dev/null @@ -1,96 +0,0 @@ -import logging - -from agent_protocol import StepHandler, StepResult -from forge.config.ai_profile import AIProfile -from forge.config.config import ConfigBuilder -from forge.llm.prompting.prompt import DEFAULT_TRIGGERING_PROMPT -from forge.logging.helpers import user_friendly_output - -from autogpt.agents import Agent -from autogpt.app.main import UserFeedback - - -async def task_handler(task_input) -> StepHandler: - task = task_input.__root__ if task_input else {} - agent = bootstrap_agent(task.get("user_input"), False) - - next_command_name: str | None = None - next_command_args: dict[str, str] | None = None - - async def step_handler(step_input) -> StepResult: - step = step_input.__root__ if step_input else {} - - nonlocal next_command_name, next_command_args - - result = await interaction_step( - agent, - step.get("user_input"), - step.get("user_feedback"), - next_command_name, - next_command_args, - ) - - next_command_name = result["next_step_command_name"] if result else None - next_command_args = result["next_step_command_args"] if result else None - - if not result: - return StepResult(output=None, is_last=True) - return StepResult(output=result) - - return step_handler - - -async def interaction_step( - agent: Agent, - user_input, - user_feedback: UserFeedback | None, - command_name: str | None, - command_args: dict[str, str] | None, -): - """Run one step of the interaction loop.""" - if user_feedback == UserFeedback.EXIT: - return - if user_feedback == UserFeedback.TEXT: - command_name = "human_feedback" - - result: str | None = None - - if command_name is not None: - result = agent.execute(command_name, command_args, user_input) - if result is None: - user_friendly_output( - title="SYSTEM:", message="Unable to execute command", level=logging.WARN - ) - return - - next_command_name, next_command_args, assistant_reply_dict = agent.propose_action() - - return { - "config": agent.config, - "ai_profile": agent.ai_profile, - "result": result, - "assistant_reply_dict": assistant_reply_dict, - "next_step_command_name": next_command_name, - "next_step_command_args": next_command_args, - } - - -def bootstrap_agent(task, continuous_mode) -> Agent: - config = ConfigBuilder.build_config_from_env() - config.logging.level = logging.DEBUG - config.logging.plain_console_output = True - config.continuous_mode = continuous_mode - config.temperature = 0 - config.memory_backend = "no_memory" - ai_profile = AIProfile( - ai_name="AutoGPT", - ai_role="a multi-purpose AI assistant.", - ai_goals=[task], - ) - # FIXME this won't work - ai_profile and triggering_prompt is not a valid argument, - # lacks file_storage, settings and llm_provider - return Agent( - ai_profile=ai_profile, - legacy_config=config, - triggering_prompt=DEFAULT_TRIGGERING_PROMPT, - ) diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/__init__.py b/autogpts/autogpt/autogpt/core/runner/client_lib/logging/__init__.py deleted file mode 100644 index 6d263b6ad1..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging - -from .config import BelowLevelFilter, FancyConsoleFormatter, configure_root_logger -from .helpers import dump_prompt - - -def get_client_logger(): - # Configure logging before we do anything else. - # Application logs need a place to live. - client_logger = logging.getLogger("autogpt_client_application") - client_logger.setLevel(logging.DEBUG) - - return client_logger - - -__all__ = [ - "configure_root_logger", - "get_client_logger", - "FancyConsoleFormatter", - "BelowLevelFilter", - "dump_prompt", -] diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/config.py b/autogpts/autogpt/autogpt/core/runner/client_lib/logging/config.py deleted file mode 100644 index 912b0751f0..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/config.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -import sys - -from forge.logging import BelowLevelFilter, FancyConsoleFormatter -from openai._base_client import log as openai_logger - -SIMPLE_LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s" -DEBUG_LOG_FORMAT = ( - "%(asctime)s.%(msecs)03d %(levelname)s %(filename)s:%(lineno)d %(message)s" -) - - -def configure_root_logger(): - console_formatter = FancyConsoleFormatter(SIMPLE_LOG_FORMAT) - - stdout = logging.StreamHandler(stream=sys.stdout) - stdout.setLevel(logging.DEBUG) - stdout.addFilter(BelowLevelFilter(logging.WARNING)) - stdout.setFormatter(console_formatter) - stderr = logging.StreamHandler() - stderr.setLevel(logging.WARNING) - stderr.setFormatter(console_formatter) - - logging.basicConfig(level=logging.DEBUG, handlers=[stdout, stderr]) - - # Disable debug logging from OpenAI library - openai_logger.setLevel(logging.WARNING) diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/helpers.py b/autogpts/autogpt/autogpt/core/runner/client_lib/logging/helpers.py deleted file mode 100644 index 4aa908afa5..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/logging/helpers.py +++ /dev/null @@ -1,28 +0,0 @@ -from math import ceil, floor -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from forge.llm.prompting import ChatPrompt - from forge.llm.providers.schema import ChatMessage - - -SEPARATOR_LENGTH = 42 - - -def dump_prompt(prompt: "ChatPrompt | list[ChatMessage]") -> str: - def separator(text: str): - half_sep_len = (SEPARATOR_LENGTH - 2 - len(text)) / 2 - return f"{floor(half_sep_len)*'-'} {text.upper()} {ceil(half_sep_len)*'-'}" - - if not isinstance(prompt, list): - prompt = prompt.messages - - formatted_messages = "\n".join( - [f"{separator(m.role)}\n{m.content}" for m in prompt] - ) - return f""" -============== {prompt.__class__.__name__} ============== -Length: {len(prompt)} messages -{formatted_messages} -========================================== -""" diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/parser.py b/autogpts/autogpt/autogpt/core/runner/client_lib/parser.py deleted file mode 100755 index 54af17403e..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/parser.py +++ /dev/null @@ -1,45 +0,0 @@ -def parse_agent_name_and_goals(name_and_goals: dict) -> str: - parsed_response = f"Agent Name: {name_and_goals['agent_name']}\n" - parsed_response += f"Agent Role: {name_and_goals['agent_role']}\n" - parsed_response += "Agent Goals:\n" - for i, goal in enumerate(name_and_goals["agent_goals"]): - parsed_response += f"{i+1}. {goal}\n" - return parsed_response - - -def parse_agent_plan(plan: dict) -> str: - parsed_response = "Agent Plan:\n" - for i, task in enumerate(plan["task_list"]): - parsed_response += f"{i+1}. {task['objective']}\n" - parsed_response += f"Task type: {task['type']} " - parsed_response += f"Priority: {task['priority']}\n" - parsed_response += "Ready Criteria:\n" - for j, criteria in enumerate(task["ready_criteria"]): - parsed_response += f" {j+1}. {criteria}\n" - parsed_response += "Acceptance Criteria:\n" - for j, criteria in enumerate(task["acceptance_criteria"]): - parsed_response += f" {j+1}. {criteria}\n" - parsed_response += "\n" - - return parsed_response - - -def parse_next_ability(current_task, next_ability: dict) -> str: - parsed_response = f"Current Task: {current_task.objective}\n" - ability_args = ", ".join( - f"{k}={v}" for k, v in next_ability["ability_arguments"].items() - ) - parsed_response += f"Next Ability: {next_ability['next_ability']}({ability_args})\n" - parsed_response += f"Motivation: {next_ability['motivation']}\n" - parsed_response += f"Self-criticism: {next_ability['self_criticism']}\n" - parsed_response += f"Reasoning: {next_ability['reasoning']}\n" - return parsed_response - - -def parse_ability_result(ability_result) -> str: - parsed_response = f"Ability: {ability_result['ability_name']}\n" - parsed_response += f"Ability Arguments: {ability_result['ability_args']}\n" - parsed_response += f"Ability Result: {ability_result['success']}\n" - parsed_response += f"Message: {ability_result['message']}\n" - parsed_response += f"Data: {ability_result['new_knowledge']}\n" - return parsed_response diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/settings.py b/autogpts/autogpt/autogpt/core/runner/client_lib/settings.py deleted file mode 100644 index 9c99830240..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/settings.py +++ /dev/null @@ -1,14 +0,0 @@ -from pathlib import Path - -import yaml - -from autogpt.core.agent import SimpleAgent - - -def make_user_configuration(settings_file_path: Path): - user_configuration = SimpleAgent.build_user_configuration() - - settings_file_path.parent.mkdir(parents=True, exist_ok=True) - print("Writing settings to", settings_file_path) - with settings_file_path.open("w") as f: - yaml.safe_dump(user_configuration, f) diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/shared_click_commands.py b/autogpts/autogpt/autogpt/core/runner/client_lib/shared_click_commands.py deleted file mode 100644 index 5be52acb83..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/shared_click_commands.py +++ /dev/null @@ -1,19 +0,0 @@ -import pathlib - -import click - -DEFAULT_SETTINGS_FILE = str( - pathlib.Path("~/auto-gpt/default_agent_settings.yml").expanduser() -) - - -@click.command() -@click.option( - "--settings-file", - type=click.Path(), - default=DEFAULT_SETTINGS_FILE, -) -def make_settings(settings_file: str) -> None: - from autogpt.core.runner.client_lib.settings import make_user_configuration - - make_user_configuration(pathlib.Path(settings_file)) diff --git a/autogpts/autogpt/autogpt/core/runner/client_lib/utils.py b/autogpts/autogpt/autogpt/core/runner/client_lib/utils.py deleted file mode 100644 index 887683df7c..0000000000 --- a/autogpts/autogpt/autogpt/core/runner/client_lib/utils.py +++ /dev/null @@ -1,62 +0,0 @@ -import asyncio -import functools -from bdb import BdbQuit -from typing import Any, Callable, Coroutine, ParamSpec, TypeVar - -import click - -P = ParamSpec("P") -T = TypeVar("T") - - -def handle_exceptions( - application_main: Callable[P, T], - with_debugger: bool, -) -> Callable[P, T]: - """Wraps a function so that it drops a user into a debugger if it raises an error. - - This is intended to be used as a wrapper for the main function of a CLI application. - It will catch all errors and drop a user into a debugger if the error is not a - `KeyboardInterrupt`. If the error is a `KeyboardInterrupt`, it will raise the error. - If the error is not a `KeyboardInterrupt`, it will log the error and drop a user - into a debugger if `with_debugger` is `True`. - If `with_debugger` is `False`, it will raise the error. - - Parameters - ---------- - application_main - The function to wrap. - with_debugger - Whether to drop a user into a debugger if an error is raised. - - Returns - ------- - Callable - The wrapped function. - - """ - - @functools.wraps(application_main) - async def wrapped(*args: P.args, **kwargs: P.kwargs) -> T: - try: - return await application_main(*args, **kwargs) - except (BdbQuit, KeyboardInterrupt, click.Abort): - raise - except Exception as e: - if with_debugger: - print(f"Uncaught exception {e}") - import pdb - - pdb.post_mortem() - else: - raise - - return wrapped - - -def coroutine(f: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, T]: - @functools.wraps(f) - def wrapper(*args: P.args, **kwargs: P.kwargs): - return asyncio.run(f(*args, **kwargs)) - - return wrapper diff --git a/autogpts/autogpt/autogpt/core/workspace/__init__.py b/autogpts/autogpt/autogpt/core/workspace/__init__.py deleted file mode 100644 index ae1877dee0..0000000000 --- a/autogpts/autogpt/autogpt/core/workspace/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""The workspace is the central hub for the Agent's on disk resources.""" -from autogpt.core.workspace.base import Workspace -from autogpt.core.workspace.simple import SimpleWorkspace, WorkspaceSettings - -__all__ = [ - "SimpleWorkspace", - "Workspace", - "WorkspaceSettings", -] diff --git a/autogpts/autogpt/autogpt/core/workspace/base.py b/autogpts/autogpt/autogpt/core/workspace/base.py deleted file mode 100644 index b3f1a9fc82..0000000000 --- a/autogpts/autogpt/autogpt/core/workspace/base.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import abc -import logging -import typing -from pathlib import Path - -if typing.TYPE_CHECKING: - from autogpt.core.agent.simple import AgentConfiguration - - -class Workspace(abc.ABC): - """The workspace is the root directory for all generated files. - - The workspace is responsible for creating the root directory and - providing a method for getting the full path to an item in the - workspace. - - """ - - @property - @abc.abstractmethod - def root(self) -> Path: - """The root directory of the workspace.""" - ... - - @property - @abc.abstractmethod - def restrict_to_workspace(self) -> bool: - """Whether to restrict generated paths to the workspace.""" - ... - - @staticmethod - @abc.abstractmethod - def setup_workspace( - configuration: AgentConfiguration, logger: logging.Logger - ) -> Path: - """Create the workspace root directory and set up all initial content. - - Parameters - ---------- - configuration - The Agent's configuration. - logger - The Agent's logger. - - Returns - ------- - Path - The path to the workspace root directory. - - """ - ... - - @abc.abstractmethod - def get_path(self, relative_path: str | Path) -> Path: - """Get the full path for an item in the workspace. - - Parameters - ---------- - relative_path - The path to the item relative to the workspace root. - - Returns - ------- - Path - The full path to the item. - - """ - ... diff --git a/autogpts/autogpt/autogpt/core/workspace/simple.py b/autogpts/autogpt/autogpt/core/workspace/simple.py deleted file mode 100644 index de1eb9364c..0000000000 --- a/autogpts/autogpt/autogpt/core/workspace/simple.py +++ /dev/null @@ -1,194 +0,0 @@ -import json -import logging -import typing -from pathlib import Path - -from forge.models.config import ( - Configurable, - SystemConfiguration, - SystemSettings, - UserConfigurable, -) -from pydantic import SecretField - -from autogpt.core.workspace.base import Workspace - -if typing.TYPE_CHECKING: - # Cyclic import - from autogpt.core.agent.simple import AgentSettings - - -class WorkspaceConfiguration(SystemConfiguration): - root: str - parent: str = UserConfigurable() - restrict_to_workspace: bool = UserConfigurable() - - -class WorkspaceSettings(SystemSettings): - configuration: WorkspaceConfiguration - - -class SimpleWorkspace(Configurable, Workspace): - default_settings = WorkspaceSettings( - name="workspace", - description="The workspace is the root directory for all agent activity.", - configuration=WorkspaceConfiguration( - root="", - parent="~/auto-gpt/agents", - restrict_to_workspace=True, - ), - ) - - NULL_BYTES = ["\0", "\000", "\x00", "\u0000", "%00"] - - def __init__( - self, - settings: WorkspaceSettings, - logger: logging.Logger, - ): - self._configuration = settings.configuration - self._logger = logger.getChild("workspace") - - @property - def root(self) -> Path: - return Path(self._configuration.root) - - @property - def debug_log_path(self) -> Path: - return self.root / "logs" / "debug.log" - - @property - def cycle_log_path(self) -> Path: - return self.root / "logs" / "cycle.log" - - @property - def configuration_path(self) -> Path: - return self.root / "configuration.yml" - - @property - def restrict_to_workspace(self) -> bool: - return self._configuration.restrict_to_workspace - - def get_path(self, relative_path: str | Path) -> Path: - """Get the full path for an item in the workspace. - - Parameters - ---------- - relative_path - The relative path to resolve in the workspace. - - Returns - ------- - Path - The resolved path relative to the workspace. - - """ - return self._sanitize_path( - relative_path, - root=self.root, - restrict_to_root=self.restrict_to_workspace, - ) - - def _sanitize_path( - self, - relative_path: str | Path, - root: str | Path = None, - restrict_to_root: bool = True, - ) -> Path: - """Resolve the relative path within the given root if possible. - - Parameters - ---------- - relative_path - The relative path to resolve. - root - The root path to resolve the relative path within. - restrict_to_root - Whether to restrict the path to the root. - - Returns - ------- - Path - The resolved path. - - Raises - ------ - ValueError - If the path is absolute and a root is provided. - ValueError - If the path is outside the root and the root is restricted. - - """ - - # Posix systems disallow null bytes in paths. Windows is agnostic about it. - # Do an explicit check here for all sorts of null byte representations. - - for null_byte in self.NULL_BYTES: - if null_byte in str(relative_path) or null_byte in str(root): - raise ValueError("embedded null byte") - - if root is None: - return Path(relative_path).resolve() - - self._logger.debug(f"Resolving path '{relative_path}' in workspace '{root}'") - root, relative_path = Path(root).resolve(), Path(relative_path) - self._logger.debug(f"Resolved root as '{root}'") - - if relative_path.is_absolute(): - raise ValueError( - f"Attempted to access absolute path '{relative_path}' " - f"in workspace '{root}'." - ) - full_path = root.joinpath(relative_path).resolve() - - self._logger.debug(f"Joined paths as '{full_path}'") - - if restrict_to_root and not full_path.is_relative_to(root): - raise ValueError( - f"Attempted to access path '{full_path}' outside of workspace '{root}'." - ) - - return full_path - - ################################### - # Factory methods for agent setup # - ################################### - - @staticmethod - def setup_workspace(settings: "AgentSettings", logger: logging.Logger) -> Path: - workspace_parent = settings.workspace.configuration.parent - workspace_parent = Path(workspace_parent).expanduser().resolve() - workspace_parent.mkdir(parents=True, exist_ok=True) - - agent_name = settings.agent.name - - workspace_root = workspace_parent / agent_name - workspace_root.mkdir(parents=True, exist_ok=True) - - settings.workspace.configuration.root = str(workspace_root) - - with (workspace_root / "agent_settings.json").open("w") as f: - settings_json = settings.json( - encoder=lambda x: x.get_secret_value() - if isinstance(x, SecretField) - else x, - ) - f.write(settings_json) - - # TODO: What are all the kinds of logs we want here? - log_path = workspace_root / "logs" - log_path.mkdir(parents=True, exist_ok=True) - (log_path / "debug.log").touch() - (log_path / "cycle.log").touch() - - return workspace_root - - @staticmethod - def load_agent_settings(workspace_root: Path) -> "AgentSettings": - # Cyclic import - from autogpt.core.agent.simple import AgentSettings - - with (workspace_root / "agent_settings.json").open("r") as f: - agent_settings = json.load(f) - - return AgentSettings.parse_obj(agent_settings) diff --git a/autogpts/autogpt/autogpt/memory/vector/__init__.py b/autogpts/autogpt/autogpt/memory/vector/__init__.py deleted file mode 100644 index 2f9e121ac6..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/__init__.py +++ /dev/null @@ -1,156 +0,0 @@ -from forge.config.config import Config - -from .memory_item import MemoryItem, MemoryItemFactory, MemoryItemRelevance -from .providers.base import VectorMemoryProvider as VectorMemory -from .providers.json_file import JSONFileMemory -from .providers.no_memory import NoMemory - -# List of supported memory backends -# Add a backend to this list if the import attempt is successful -supported_memory = ["json_file", "no_memory"] - -# try: -# from .providers.redis import RedisMemory - -# supported_memory.append("redis") -# except ImportError: -# RedisMemory = None - -# try: -# from .providers.pinecone import PineconeMemory - -# supported_memory.append("pinecone") -# except ImportError: -# PineconeMemory = None - -# try: -# from .providers.weaviate import WeaviateMemory - -# supported_memory.append("weaviate") -# except ImportError: -# WeaviateMemory = None - -# try: -# from .providers.milvus import MilvusMemory - -# supported_memory.append("milvus") -# except ImportError: -# MilvusMemory = None - - -def get_memory(config: Config) -> VectorMemory: - """ - Returns a memory object corresponding to the memory backend specified in the config. - - The type of memory object returned depends on the value of the `memory_backend` - attribute in the configuration. E.g. if `memory_backend` is set to "pinecone", a - `PineconeMemory` object is returned. If it is set to "redis", a `RedisMemory` - object is returned. - By default, a `JSONFileMemory` object is returned. - - Params: - config: A configuration object that contains information about the memory - backend to be used and other relevant parameters. - - Returns: - VectorMemory: an instance of a memory object based on the configuration provided - """ - memory = None - - match config.memory_backend: - case "json_file": - memory = JSONFileMemory(config) - - case "pinecone": - raise NotImplementedError( - "The Pinecone memory backend has been rendered incompatible by work on " - "the memory system, and was removed. Whether support will be added " - "back in the future is subject to discussion, feel free to pitch in: " - "https://github.com/Significant-Gravitas/AutoGPT/discussions/4280" - ) - # if not PineconeMemory: - # logger.warning( - # "Error: Pinecone is not installed. Please install pinecone" - # " to use Pinecone as a memory backend." - # ) - # else: - # memory = PineconeMemory(config) - # if clear: - # memory.clear() - - case "redis": - raise NotImplementedError( - "The Redis memory backend has been rendered incompatible by work on " - "the memory system, and has been removed temporarily." - ) - # if not RedisMemory: - # logger.warning( - # "Error: Redis is not installed. Please install redis-py to" - # " use Redis as a memory backend." - # ) - # else: - # memory = RedisMemory(config) - - case "weaviate": - raise NotImplementedError( - "The Weaviate memory backend has been rendered incompatible by work on " - "the memory system, and was removed. Whether support will be added " - "back in the future is subject to discussion, feel free to pitch in: " - "https://github.com/Significant-Gravitas/AutoGPT/discussions/4280" - ) - # if not WeaviateMemory: - # logger.warning( - # "Error: Weaviate is not installed. Please install weaviate-client" - # " to use Weaviate as a memory backend." - # ) - # else: - # memory = WeaviateMemory(config) - - case "milvus": - raise NotImplementedError( - "The Milvus memory backend has been rendered incompatible by work on " - "the memory system, and was removed. Whether support will be added " - "back in the future is subject to discussion, feel free to pitch in: " - "https://github.com/Significant-Gravitas/AutoGPT/discussions/4280" - ) - # if not MilvusMemory: - # logger.warning( - # "Error: pymilvus sdk is not installed, but required " - # "to use Milvus or Zilliz as memory backend. " - # "Please install pymilvus." - # ) - # else: - # memory = MilvusMemory(config) - - case "no_memory": - memory = NoMemory() - - case _: - raise ValueError( - f"Unknown memory backend '{config.memory_backend}'." - " Please check your config." - ) - - if memory is None: - memory = JSONFileMemory(config) - - return memory - - -def get_supported_memory_backends(): - return supported_memory - - -__all__ = [ - "get_memory", - "MemoryItem", - "MemoryItemFactory", - "MemoryItemRelevance", - "JSONFileMemory", - "NoMemory", - "VectorMemory", - # "RedisMemory", - # "PineconeMemory", - # "MilvusMemory", - # "WeaviateMemory", -] diff --git a/autogpts/autogpt/autogpt/memory/vector/memory_item.py b/autogpts/autogpt/autogpt/memory/vector/memory_item.py deleted file mode 100644 index 3de65fe257..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/memory_item.py +++ /dev/null @@ -1,280 +0,0 @@ -from __future__ import annotations - -import json -import logging -from typing import Literal - -import ftfy -import numpy as np -from forge.config.config import Config -from forge.content_processing.text import chunk_content, split_text, summarize_text -from forge.llm.providers import ChatMessage, ChatModelProvider, EmbeddingModelProvider -from pydantic import BaseModel - -from .utils import Embedding, get_embedding - -logger = logging.getLogger(__name__) - -MemoryDocType = Literal["webpage", "text_file", "code_file", "agent_history"] - - -class MemoryItem(BaseModel, arbitrary_types_allowed=True): - """Memory object containing raw content as well as embeddings""" - - raw_content: str - summary: str - chunks: list[str] - chunk_summaries: list[str] - e_summary: Embedding - e_chunks: list[Embedding] - metadata: dict - - def relevance_for(self, query: str, e_query: Embedding | None = None): - return MemoryItemRelevance.of(self, query, e_query) - - def dump(self, calculate_length=False) -> str: - n_chunks = len(self.e_chunks) - return f""" -=============== MemoryItem =============== -Size: {n_chunks} chunks -Metadata: {json.dumps(self.metadata, indent=2)} ----------------- SUMMARY ----------------- -{self.summary} ------------------- RAW ------------------- -{self.raw_content} -========================================== -""" - - def __eq__(self, other: MemoryItem): - return ( - self.raw_content == other.raw_content - and self.chunks == other.chunks - and self.chunk_summaries == other.chunk_summaries - # Embeddings can either be list[float] or np.ndarray[float32], - # and for comparison they must be of the same type - and np.array_equal( - self.e_summary - if isinstance(self.e_summary, np.ndarray) - else np.array(self.e_summary, dtype=np.float32), - other.e_summary - if isinstance(other.e_summary, np.ndarray) - else np.array(other.e_summary, dtype=np.float32), - ) - and np.array_equal( - self.e_chunks - if isinstance(self.e_chunks[0], np.ndarray) - else [np.array(c, dtype=np.float32) for c in self.e_chunks], - other.e_chunks - if isinstance(other.e_chunks[0], np.ndarray) - else [np.array(c, dtype=np.float32) for c in other.e_chunks], - ) - ) - - -class MemoryItemFactory: - def __init__( - self, - llm_provider: ChatModelProvider, - embedding_provider: EmbeddingModelProvider, - ): - self.llm_provider = llm_provider - self.embedding_provider = embedding_provider - - async def from_text( - self, - text: str, - source_type: MemoryDocType, - config: Config, - metadata: dict = {}, - how_to_summarize: str | None = None, - question_for_summary: str | None = None, - ): - logger.debug(f"Memorizing text:\n{'-'*32}\n{text}\n{'-'*32}\n") - - # Fix encoding, e.g. removing unicode surrogates (see issue #778) - text = ftfy.fix_text(text) - - # FIXME: needs ModelProvider - chunks = [ - chunk - for chunk, _ in ( - split_text( - text=text, - config=config, - max_chunk_length=1000, # arbitrary, but shorter ~= better - tokenizer=self.llm_provider.get_tokenizer(config.fast_llm), - ) - if source_type != "code_file" - # TODO: chunk code based on structure/outline - else chunk_content( - content=text, - max_chunk_length=1000, - tokenizer=self.llm_provider.get_tokenizer(config.fast_llm), - ) - ) - ] - logger.debug("Chunks: " + str(chunks)) - - chunk_summaries = [ - summary - for summary, _ in [ - await summarize_text( - text=text_chunk, - instruction=how_to_summarize, - question=question_for_summary, - llm_provider=self.llm_provider, - config=config, - ) - for text_chunk in chunks - ] - ] - logger.debug("Chunk summaries: " + str(chunk_summaries)) - - e_chunks = get_embedding(chunks, config, self.embedding_provider) - - summary = ( - chunk_summaries[0] - if len(chunks) == 1 - else ( - await summarize_text( - text="\n\n".join(chunk_summaries), - instruction=how_to_summarize, - question=question_for_summary, - llm_provider=self.llm_provider, - config=config, - ) - )[0] - ) - logger.debug("Total summary: " + summary) - - # TODO: investigate search performance of weighted average vs summary - # e_average = np.average(e_chunks, axis=0, weights=[len(c) for c in chunks]) - e_summary = get_embedding(summary, config, self.embedding_provider) - - metadata["source_type"] = source_type - - return MemoryItem( - raw_content=text, - summary=summary, - chunks=chunks, - chunk_summaries=chunk_summaries, - e_summary=e_summary, - e_chunks=e_chunks, - metadata=metadata, - ) - - def from_text_file(self, content: str, path: str, config: Config): - return self.from_text(content, "text_file", config, {"location": path}) - - def from_code_file(self, content: str, path: str): - # TODO: implement tailored code memories - return self.from_text(content, "code_file", {"location": path}) - - def from_ai_action(self, ai_message: ChatMessage, result_message: ChatMessage): - # The result_message contains either user feedback - # or the result of the command specified in ai_message - - if ai_message.role != "assistant": - raise ValueError(f"Invalid role on 'ai_message': {ai_message.role}") - - result = ( - result_message.content - if result_message.content.startswith("Command") - else "None" - ) - user_input = ( - result_message.content - if result_message.content.startswith("Human feedback") - else "None" - ) - memory_content = ( - f"Assistant Reply: {ai_message.content}" - "\n\n" - f"Result: {result}" - "\n\n" - f"Human Feedback: {user_input}" - ) - - return self.from_text( - text=memory_content, - source_type="agent_history", - how_to_summarize=( - "if possible, also make clear the link between the command in the" - " assistant's response and the command result. " - "Do not mention the human feedback if there is none.", - ), - ) - - def from_webpage( - self, content: str, url: str, config: Config, question: str | None = None - ): - return self.from_text( - text=content, - source_type="webpage", - config=config, - metadata={"location": url}, - question_for_summary=question, - ) - - -class MemoryItemRelevance(BaseModel): - """ - Class that encapsulates memory relevance search functionality and data. - Instances contain a MemoryItem and its relevance scores for a given query. - """ - - memory_item: MemoryItem - for_query: str - summary_relevance_score: float - chunk_relevance_scores: list[float] - - @staticmethod - def of( - memory_item: MemoryItem, for_query: str, e_query: Embedding | None = None - ) -> MemoryItemRelevance: - e_query = e_query if e_query is not None else get_embedding(for_query) - _, srs, crs = MemoryItemRelevance.calculate_scores(memory_item, e_query) - return MemoryItemRelevance( - for_query=for_query, - memory_item=memory_item, - summary_relevance_score=srs, - chunk_relevance_scores=crs, - ) - - @staticmethod - def calculate_scores( - memory: MemoryItem, compare_to: Embedding - ) -> tuple[float, float, list[float]]: - """ - Calculates similarity between given embedding and all embeddings of the memory - - Returns: - float: the aggregate (max) relevance score of the memory - float: the relevance score of the memory summary - list: the relevance scores of the memory chunks - """ - summary_relevance_score = np.dot(memory.e_summary, compare_to) - chunk_relevance_scores = np.dot(memory.e_chunks, compare_to).tolist() - logger.debug(f"Relevance of summary: {summary_relevance_score}") - logger.debug(f"Relevance of chunks: {chunk_relevance_scores}") - - relevance_scores = [summary_relevance_score, *chunk_relevance_scores] - logger.debug(f"Relevance scores: {relevance_scores}") - return max(relevance_scores), summary_relevance_score, chunk_relevance_scores - - @property - def score(self) -> float: - """The aggregate relevance score of the memory item for the given query""" - return max([self.summary_relevance_score, *self.chunk_relevance_scores]) - - @property - def most_relevant_chunk(self) -> tuple[str, float]: - """The most relevant chunk of the memory item + its score for the given query""" - i_relmax = np.argmax(self.chunk_relevance_scores) - return self.memory_item.chunks[i_relmax], self.chunk_relevance_scores[i_relmax] - - def __str__(self): - return ( - f"{self.memory_item.summary} ({self.summary_relevance_score}) " - f"{self.chunk_relevance_scores}" - ) diff --git a/autogpts/autogpt/autogpt/memory/vector/providers/__init__.py b/autogpts/autogpt/autogpt/memory/vector/providers/__init__.py deleted file mode 100644 index 12a23b6000..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/providers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .json_file import JSONFileMemory -from .no_memory import NoMemory - -__all__ = [ - "JSONFileMemory", - "NoMemory", -] diff --git a/autogpts/autogpt/autogpt/memory/vector/providers/base.py b/autogpts/autogpt/autogpt/memory/vector/providers/base.py deleted file mode 100644 index b227840f19..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/providers/base.py +++ /dev/null @@ -1,78 +0,0 @@ -import abc -import functools -import logging -from typing import MutableSet, Sequence - -import numpy as np -from forge.config.config import Config - -from .. import MemoryItem, MemoryItemRelevance -from ..utils import Embedding, get_embedding - -logger = logging.getLogger(__name__) - - -class VectorMemoryProvider(MutableSet[MemoryItem]): - @abc.abstractmethod - def __init__(self, config: Config): - pass - - def get(self, query: str, config: Config) -> MemoryItemRelevance | None: - """ - Gets the data from the memory that is most relevant to the given query. - - Args: - query: The query used to retrieve information. - config: The config Object. - - Returns: The most relevant Memory - """ - result = self.get_relevant(query, 1, config) - return result[0] if result else None - - def get_relevant( - self, query: str, k: int, config: Config - ) -> Sequence[MemoryItemRelevance]: - """ - Returns the top-k most relevant memories for the given query - - Args: - query: the query to compare stored memories to - k: the number of relevant memories to fetch - config: The config Object. - - Returns: - list[MemoryItemRelevance] containing the top [k] relevant memories - """ - if len(self) < 1: - return [] - - logger.debug( - f"Searching for {k} relevant memories for query '{query}'; " - f"{len(self)} memories in index" - ) - - relevances = self.score_memories_for_relevance(query, config) - logger.debug(f"Memory relevance scores: {[str(r) for r in relevances]}") - - # take last k items and reverse - top_k_indices = np.argsort([r.score for r in relevances])[-k:][::-1] - - return [relevances[i] for i in top_k_indices] - - def score_memories_for_relevance( - self, for_query: str, config: Config - ) -> Sequence[MemoryItemRelevance]: - """ - Returns MemoryItemRelevance for every memory in the index. - Implementations may override this function for performance purposes. - """ - e_query: Embedding = get_embedding(for_query, config) - return [m.relevance_for(for_query, e_query) for m in self] - - def get_stats(self) -> tuple[int, int]: - """ - Returns: - tuple (n_memories: int, n_chunks: int): the stats of the memory index - """ - return len(self), functools.reduce(lambda t, m: t + len(m.e_chunks), self, 0) diff --git a/autogpts/autogpt/autogpt/memory/vector/providers/json_file.py b/autogpts/autogpt/autogpt/memory/vector/providers/json_file.py deleted file mode 100644 index 7fe6d5fb89..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/providers/json_file.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import annotations - -import logging -from pathlib import Path -from typing import Iterator - -import orjson -from forge.config.config import Config - -from ..memory_item import MemoryItem -from .base import VectorMemoryProvider - -logger = logging.getLogger(__name__) - - -class JSONFileMemory(VectorMemoryProvider): - """Memory backend that stores memories in a JSON file""" - - SAVE_OPTIONS = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_SERIALIZE_DATACLASS - - file_path: Path - memories: list[MemoryItem] - - def __init__(self, config: Config) -> None: - """Initialize a class instance - - Args: - config: Config object - - Returns: - None - """ - self.file_path = config.workspace_path / f"{config.memory_index}.json" - self.file_path.touch() - logger.debug( - f"Initialized {__class__.__name__} with index path {self.file_path}" - ) - - self.memories = [] - try: - self.load_index() - logger.debug(f"Loaded {len(self.memories)} MemoryItems from file") - except Exception as e: - logger.warning(f"Could not load MemoryItems from file: {e}") - self.save_index() - - def __iter__(self) -> Iterator[MemoryItem]: - return iter(self.memories) - - def __contains__(self, x: MemoryItem) -> bool: - return x in self.memories - - def __len__(self) -> int: - return len(self.memories) - - def add(self, item: MemoryItem): - self.memories.append(item) - logger.debug(f"Adding item to memory: {item.dump()}") - self.save_index() - return len(self.memories) - - def discard(self, item: MemoryItem): - try: - self.remove(item) - except ValueError: # item not in memory - pass - - def clear(self): - """Clears the data in memory.""" - self.memories.clear() - self.save_index() - - def load_index(self): - """Loads all memories from the index file""" - if not self.file_path.is_file(): - logger.debug(f"Index file '{self.file_path}' does not exist") - return - with self.file_path.open("r") as f: - logger.debug(f"Loading memories from index file '{self.file_path}'") - json_index = orjson.loads(f.read()) - for memory_item_dict in json_index: - self.memories.append(MemoryItem.parse_obj(memory_item_dict)) - - def save_index(self): - logger.debug(f"Saving memory index to file {self.file_path}") - with self.file_path.open("wb") as f: - return f.write( - orjson.dumps( - [m.dict() for m in self.memories], option=self.SAVE_OPTIONS - ) - ) diff --git a/autogpts/autogpt/autogpt/memory/vector/providers/no_memory.py b/autogpts/autogpt/autogpt/memory/vector/providers/no_memory.py deleted file mode 100644 index 9b9e92d915..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/providers/no_memory.py +++ /dev/null @@ -1,36 +0,0 @@ -"""A class that does not store any data. This is the default memory provider.""" -from __future__ import annotations - -from typing import Iterator, Optional - -from forge.config.config import Config - -from .. import MemoryItem -from .base import VectorMemoryProvider - - -class NoMemory(VectorMemoryProvider): - """ - A class that does not store any data. This is the default memory provider. - """ - - def __init__(self, config: Optional[Config] = None): - pass - - def __iter__(self) -> Iterator[MemoryItem]: - return iter([]) - - def __contains__(self, x: MemoryItem) -> bool: - return False - - def __len__(self) -> int: - return 0 - - def add(self, item: MemoryItem): - pass - - def discard(self, item: MemoryItem): - pass - - def clear(self): - pass diff --git a/autogpts/autogpt/autogpt/memory/vector/utils.py b/autogpts/autogpt/autogpt/memory/vector/utils.py deleted file mode 100644 index 05ebf51d40..0000000000 --- a/autogpts/autogpt/autogpt/memory/vector/utils.py +++ /dev/null @@ -1,79 +0,0 @@ -import logging -from typing import Any, Sequence, overload - -import numpy as np -from forge.config.config import Config -from forge.llm.providers import EmbeddingModelProvider - -logger = logging.getLogger(__name__) - -Embedding = list[float] | list[np.float32] | np.ndarray[Any, np.dtype[np.float32]] -"""Embedding vector""" - -TText = Sequence[int] -"""Tokenized text""" - - -@overload -async def get_embedding( - input: str | TText, config: Config, embedding_provider: EmbeddingModelProvider -) -> Embedding: - ... - - -@overload -async def get_embedding( - input: list[str] | list[TText], - config: Config, - embedding_provider: EmbeddingModelProvider, -) -> list[Embedding]: - ... - - -async def get_embedding( - input: str | TText | list[str] | list[TText], - config: Config, - embedding_provider: EmbeddingModelProvider, -) -> Embedding | list[Embedding]: - """Get an embedding from the ada model. - - Args: - input: Input text to get embeddings for, encoded as a string or array of tokens. - Multiple inputs may be given as a list of strings or token arrays. - embedding_provider: The provider to create embeddings. - - Returns: - List[float]: The embedding. - """ - multiple = isinstance(input, list) and all(not isinstance(i, int) for i in input) - - if isinstance(input, str): - input = input.replace("\n", " ") - elif multiple and isinstance(input[0], str): - input = [text.replace("\n", " ") for text in input] - - model = config.embedding_model - - logger.debug( - f"Getting embedding{f's for {len(input)} inputs' if multiple else ''}" - f" with model '{model}'" - ) - - if not multiple: - return ( - await embedding_provider.create_embedding( - text=input, - model_name=model, - embedding_parser=lambda e: e, - ) - ).embedding - else: - embeddings = [] - for text in input: - result = await embedding_provider.create_embedding( - text=text, - model_name=model, - embedding_parser=lambda e: e, - ) - embeddings.append(result.embedding) - return embeddings diff --git a/autogpts/autogpt/challenges_already_beaten.json b/autogpts/autogpt/challenges_already_beaten.json deleted file mode 100644 index 7bdab6f24a..0000000000 --- a/autogpts/autogpt/challenges_already_beaten.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "TestWriteFile": true -} \ No newline at end of file diff --git a/autogpts/autogpt/prompt_settings.yaml b/autogpts/autogpt/prompt_settings.yaml deleted file mode 100644 index 40ae1f8be2..0000000000 --- a/autogpts/autogpt/prompt_settings.yaml +++ /dev/null @@ -1,15 +0,0 @@ -constraints: [ - 'Exclusively use the commands listed below.', - 'You can only act proactively, and are unable to start background jobs or set up webhooks for yourself. Take this into account when planning your actions.', - 'You are unable to interact with physical objects. If this is absolutely necessary to fulfill a task or objective or to complete a step, you must ask the user to do it for you. If the user refuses this, and there is no other way to achieve your goals, you must terminate to avoid wasting time and energy.' -] -resources: [ - 'You are a Large Language Model, trained on millions of pages of text, including a lot of factual knowledge. Make use of this factual knowledge to avoid unnecessary gathering of information.' -] -best_practices: [ - 'Continuously review and analyze your actions to ensure you are performing to the best of your abilities.', - 'Constructively self-criticize your big-picture behavior constantly.', - 'Reflect on past decisions and strategies to refine your approach.', - 'Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.', - 'Only make use of your information gathering abilities to find information that you don''t yet have knowledge of.' -] diff --git a/autogpts/autogpt/tests/memory/_test_json_file_memory.py b/autogpts/autogpt/tests/memory/_test_json_file_memory.py deleted file mode 100644 index f0420205ac..0000000000 --- a/autogpts/autogpt/tests/memory/_test_json_file_memory.py +++ /dev/null @@ -1,126 +0,0 @@ -# sourcery skip: snake-case-functions -"""Tests for JSONFileMemory class""" -import orjson -import pytest -from forge.config.config import Config -from forge.file_storage import FileStorage - -from autogpt.memory.vector import JSONFileMemory, MemoryItem - - -def test_json_memory_init_without_backing_file(config: Config, storage: FileStorage): - index_file = storage.root / f"{config.memory_index}.json" - - assert not index_file.exists() - JSONFileMemory(config) - assert index_file.exists() - assert index_file.read_text() == "[]" - - -def test_json_memory_init_with_backing_empty_file(config: Config, storage: FileStorage): - index_file = storage.root / f"{config.memory_index}.json" - index_file.touch() - - assert index_file.exists() - JSONFileMemory(config) - assert index_file.exists() - assert index_file.read_text() == "[]" - - -def test_json_memory_init_with_backing_invalid_file( - config: Config, storage: FileStorage -): - index_file = storage.root / f"{config.memory_index}.json" - index_file.touch() - - raw_data = {"texts": ["test"]} - data = orjson.dumps(raw_data, option=JSONFileMemory.SAVE_OPTIONS) - with index_file.open("wb") as f: - f.write(data) - - assert index_file.exists() - JSONFileMemory(config) - assert index_file.exists() - assert index_file.read_text() == "[]" - - -def test_json_memory_add(config: Config, memory_item: MemoryItem): - index = JSONFileMemory(config) - index.add(memory_item) - assert index.memories[0] == memory_item - - -def test_json_memory_clear(config: Config, memory_item: MemoryItem): - index = JSONFileMemory(config) - assert index.memories == [] - - index.add(memory_item) - assert index.memories[0] == memory_item, "Cannot test clear() because add() fails" - - index.clear() - assert index.memories == [] - - -def test_json_memory_get(config: Config, memory_item: MemoryItem, mock_get_embedding): - index = JSONFileMemory(config) - assert ( - index.get("test", config) is None - ), "Cannot test get() because initial index is not empty" - - index.add(memory_item) - retrieved = index.get("test", config) - assert retrieved is not None - assert retrieved.memory_item == memory_item - - -def test_json_memory_load_index(config: Config, memory_item: MemoryItem): - index = JSONFileMemory(config) - index.add(memory_item) - - try: - assert index.file_path.exists(), "index was not saved to file" - assert len(index) == 1, f"index contains {len(index)} items instead of 1" - assert index.memories[0] == memory_item, "item in index != added mock item" - except AssertionError as e: - raise ValueError(f"Setting up for load_index test failed: {e}") - - index.memories = [] - index.load_index() - - assert len(index) == 1 - assert index.memories[0] == memory_item - - -@pytest.mark.vcr -@pytest.mark.requires_openai_api_key -def test_json_memory_get_relevant(config: Config, cached_openai_client: None) -> None: - index = JSONFileMemory(config) - mem1 = MemoryItem.from_text_file("Sample text", "sample.txt", config) - mem2 = MemoryItem.from_text_file( - "Grocery list:\n- Pancake mix", "groceries.txt", config - ) - mem3 = MemoryItem.from_text_file( - "What is your favorite color?", "color.txt", config - ) - lipsum = "Lorem ipsum dolor sit amet" - mem4 = MemoryItem.from_text_file(" ".join([lipsum] * 100), "lipsum.txt", config) - index.add(mem1) - index.add(mem2) - index.add(mem3) - index.add(mem4) - - assert index.get_relevant(mem1.raw_content, 1, config)[0].memory_item == mem1 - assert index.get_relevant(mem2.raw_content, 1, config)[0].memory_item == mem2 - assert index.get_relevant(mem3.raw_content, 1, config)[0].memory_item == mem3 - assert [mr.memory_item for mr in index.get_relevant(lipsum, 2, config)] == [ - mem4, - mem1, - ] - - -def test_json_memory_get_stats(config: Config, memory_item: MemoryItem) -> None: - index = JSONFileMemory(config) - index.add(memory_item) - n_memories, n_chunks = index.get_stats() - assert n_memories == 1 - assert n_chunks == 1 diff --git a/autogpts/autogpt/tests/memory/conftest.py b/autogpts/autogpt/tests/memory/conftest.py deleted file mode 100644 index 64ac651dee..0000000000 --- a/autogpts/autogpt/tests/memory/conftest.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest - -from autogpt.memory.vector.memory_item import MemoryItem -from autogpt.memory.vector.utils import Embedding - - -@pytest.fixture -def memory_item(mock_embedding: Embedding): - return MemoryItem( - raw_content="test content", - summary="test content summary", - chunks=["test content"], - chunk_summaries=["test content summary"], - e_summary=mock_embedding, - e_chunks=[mock_embedding], - metadata={}, - ) diff --git a/autogpts/autogpt/tests/memory/utils.py b/autogpts/autogpt/tests/memory/utils.py deleted file mode 100644 index ca97a7e3fb..0000000000 --- a/autogpts/autogpt/tests/memory/utils.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy -import pytest -from forge.config.config import Config -from forge.llm.providers import OPEN_AI_EMBEDDING_MODELS -from pytest_mock import MockerFixture - -import autogpt.memory.vector.memory_item as vector_memory_item -import autogpt.memory.vector.providers.base as memory_provider_base -from autogpt.memory.vector import get_memory -from autogpt.memory.vector.utils import Embedding - - -@pytest.fixture -def embedding_dimension(config: Config): - return OPEN_AI_EMBEDDING_MODELS[config.embedding_model].embedding_dimensions - - -@pytest.fixture -def mock_embedding(embedding_dimension: int) -> Embedding: - return numpy.full((1, embedding_dimension), 0.0255, numpy.float32)[0] - - -@pytest.fixture -def mock_get_embedding(mocker: MockerFixture, mock_embedding: Embedding): - mocker.patch.object( - vector_memory_item, - "get_embedding", - return_value=mock_embedding, - ) - mocker.patch.object( - memory_provider_base, - "get_embedding", - return_value=mock_embedding, - ) - - -@pytest.fixture -def memory_none(agent_test_config: Config, mock_get_embedding): - was_memory_backend = agent_test_config.memory_backend - - agent_test_config.memory_backend = "no_memory" - yield get_memory(agent_test_config) - - agent_test_config.memory_backend = was_memory_backend diff --git a/autogpts/autogpt/tests/unit/test_ai_profile.py b/autogpts/autogpt/tests/unit/test_ai_profile.py deleted file mode 100644 index 6697bc7385..0000000000 --- a/autogpts/autogpt/tests/unit/test_ai_profile.py +++ /dev/null @@ -1,71 +0,0 @@ -from forge.config.ai_profile import AIProfile -from forge.file_storage import FileStorage - -""" -Test cases for the AIProfile class, which handles loads the AI configuration -settings from a YAML file. -""" - - -def test_goals_are_always_lists_of_strings(tmp_path): - """Test if the goals attribute is always a list of strings.""" - - yaml_content = """ -ai_goals: -- Goal 1: Make a sandwich -- Goal 2, Eat the sandwich -- Goal 3 - Go to sleep -- "Goal 4: Wake up" -ai_name: McFamished -ai_role: A hungry AI -api_budget: 0.0 -""" - ai_settings_file = tmp_path / "ai_settings.yaml" - ai_settings_file.write_text(yaml_content) - - ai_profile = AIProfile.load(ai_settings_file) - - assert len(ai_profile.ai_goals) == 4 - assert ai_profile.ai_goals[0] == "Goal 1: Make a sandwich" - assert ai_profile.ai_goals[1] == "Goal 2, Eat the sandwich" - assert ai_profile.ai_goals[2] == "Goal 3 - Go to sleep" - assert ai_profile.ai_goals[3] == "Goal 4: Wake up" - - ai_settings_file.write_text("") - ai_profile.save(ai_settings_file) - - yaml_content2 = """ai_goals: -- 'Goal 1: Make a sandwich' -- Goal 2, Eat the sandwich -- Goal 3 - Go to sleep -- 'Goal 4: Wake up' -ai_name: McFamished -ai_role: A hungry AI -api_budget: 0.0 -""" - assert ai_settings_file.read_text() == yaml_content2 - - -def test_ai_profile_file_not_exists(storage: FileStorage): - """Test if file does not exist.""" - - ai_settings_file = storage.get_path("ai_settings.yaml") - - ai_profile = AIProfile.load(str(ai_settings_file)) - assert ai_profile.ai_name == "" - assert ai_profile.ai_role == "" - assert ai_profile.ai_goals == [] - assert ai_profile.api_budget == 0.0 - - -def test_ai_profile_file_is_empty(storage: FileStorage): - """Test if file does not exist.""" - - ai_settings_file = storage.get_path("ai_settings.yaml") - ai_settings_file.write_text("") - - ai_profile = AIProfile.load(str(ai_settings_file)) - assert ai_profile.ai_name == "" - assert ai_profile.ai_role == "" - assert ai_profile.ai_goals == [] - assert ai_profile.api_budget == 0.0 diff --git a/autogpts/autogpt/tests/unit/test_prompt_config.py b/autogpts/autogpt/tests/unit/test_prompt_config.py deleted file mode 100644 index 517b478c3f..0000000000 --- a/autogpts/autogpt/tests/unit/test_prompt_config.py +++ /dev/null @@ -1,42 +0,0 @@ -from forge.config.ai_directives import AIDirectives - -""" -Test cases for the PromptConfig class, which handles loads the Prompts configuration -settings from a YAML file. -""" - - -def test_prompt_config_loading(tmp_path): - """Test if the prompt configuration loads correctly""" - - yaml_content = """ -constraints: -- A test constraint -- Another test constraint -- A third test constraint -resources: -- A test resource -- Another test resource -- A third test resource -best_practices: -- A test best-practice -- Another test best-practice -- A third test best-practice -""" - prompt_settings_file = tmp_path / "test_prompt_settings.yaml" - prompt_settings_file.write_text(yaml_content) - - prompt_config = AIDirectives.from_file(prompt_settings_file) - - assert len(prompt_config.constraints) == 3 - assert prompt_config.constraints[0] == "A test constraint" - assert prompt_config.constraints[1] == "Another test constraint" - assert prompt_config.constraints[2] == "A third test constraint" - assert len(prompt_config.resources) == 3 - assert prompt_config.resources[0] == "A test resource" - assert prompt_config.resources[1] == "Another test resource" - assert prompt_config.resources[2] == "A third test resource" - assert len(prompt_config.best_practices) == 3 - assert prompt_config.best_practices[0] == "A test best-practice" - assert prompt_config.best_practices[1] == "Another test best-practice" - assert prompt_config.best_practices[2] == "A third test best-practice" diff --git a/autogpts/forge/.flake8 b/autogpts/forge/.flake8 deleted file mode 100644 index fb435b4aa2..0000000000 --- a/autogpts/forge/.flake8 +++ /dev/null @@ -1,15 +0,0 @@ -[flake8] -max-line-length = 88 -select = "E303, W293, W292, E305, E231, E302" -exclude = - .tox, - __pycache__, - *.pyc, - .env - venv*/*, - .venv/*, - reports/*, - dist/*, - agent/*, - code, - agbenchmark/challenges/* diff --git a/autogpts/forge/.pre-commit-config.yaml b/autogpts/forge/.pre-commit-config.yaml deleted file mode 100644 index d846a08471..0000000000 --- a/autogpts/forge/.pre-commit-config.yaml +++ /dev/null @@ -1,43 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-added-large-files - args: ['--maxkb=500'] - - id: check-byte-order-marker - - id: check-case-conflict - - id: check-merge-conflict - - id: check-symlinks - - id: debug-statements - - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - language_version: python3.11 - - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - language_version: python3.11 - - # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: 'v1.3.0' - # hooks: - # - id: mypy - - - repo: local - hooks: - - id: autoflake - name: autoflake - entry: autoflake --in-place --remove-all-unused-imports --recursive --ignore-init-module-imports --ignore-pass-after-docstring forge/autogpt - language: python - types: [ python ] - # Mono repo has bronken this TODO: fix - # - id: pytest-check - # name: pytest-check - # entry: pytest - # language: system - # pass_filenames: false - # always_run: true diff --git a/autogpts/forge/forge/__main__.py b/autogpts/forge/forge/__main__.py deleted file mode 100644 index d0b1536e51..0000000000 --- a/autogpts/forge/forge/__main__.py +++ /dev/null @@ -1,57 +0,0 @@ -import os - -import uvicorn -from dotenv import load_dotenv - -import forge.sdk.forge_log - -LOG = forge.sdk.forge_log.ForgeLogger(__name__) - - -logo = """\n\n - d8888 888 .d8888b. 8888888b. 88888888888 - d88888 888 d88P Y88b 888 Y88b 888 - d88P888 888 888 888 888 888 888 - d88P 888 888 888 888888 .d88b. 888 888 d88P 888 - d88P 888 888 888 888 d88""88b 888 88888 8888888P" 888 - d88P 888 888 888 888 888 888 888 888 888 888 - d8888888888 Y88b 888 Y88b. Y88..88P Y88b d88P 888 888 -d88P 888 "Y88888 "Y888 "Y88P" "Y8888P88 888 888 - - - - 8888888888 - 888 - 888 - 8888888 .d88b. 888d888 .d88b. .d88b. - 888 d88""88b 888P" d88P"88b d8P Y8b - 888 888 888 888 888 888 88888888 - 888 Y88..88P 888 Y88b 888 Y8b. - 888 "Y88P" 888 "Y88888 "Y8888 - 888 - Y8b d88P - "Y88P" v0.1.0 -\n""" - -if __name__ == "__main__": - print(logo) - port = os.getenv("PORT", 8000) - LOG.info(f"Agent server starting on http://localhost:{port}") - load_dotenv() - forge.sdk.forge_log.setup_logger() - - uvicorn.run( - "forge.app:app", - host="localhost", - port=int(port), - log_level="error", - # Reload on changes to code or .env - reload=True, - reload_dirs=os.path.dirname(os.path.dirname(__file__)), - reload_excludes="*.py", # Cancel default *.py include pattern - reload_includes=[ - f"{os.path.basename(os.path.dirname(__file__))}/**/*.py", - ".*", - ".env", - ], - ) diff --git a/autogpts/forge/forge/actions/__init__.py b/autogpts/forge/forge/actions/__init__.py deleted file mode 100644 index 36b4d1a16e..0000000000 --- a/autogpts/forge/forge/actions/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .registry import Action, ActionParameter, ActionRegister, action diff --git a/autogpts/forge/forge/actions/file_system/files.py b/autogpts/forge/forge/actions/file_system/files.py deleted file mode 100644 index ca7abde29d..0000000000 --- a/autogpts/forge/forge/actions/file_system/files.py +++ /dev/null @@ -1,78 +0,0 @@ -from typing import List - -from ..registry import action - - -@action( - name="list_files", - description="List files in a directory", - parameters=[ - { - "name": "path", - "description": "Path to the directory", - "type": "string", - "required": True, - } - ], - output_type="list[str]", -) -async def list_files(agent, task_id: str, path: str) -> List[str]: - """ - List files in a workspace directory - """ - return agent.workspace.list(task_id=task_id, path=str(path)) - - -@action( - name="write_file", - description="Write data to a file", - parameters=[ - { - "name": "file_path", - "description": "Path to the file", - "type": "string", - "required": True, - }, - { - "name": "data", - "description": "Data to write to the file", - "type": "bytes", - "required": True, - }, - ], - output_type="None", -) -async def write_file(agent, task_id: str, file_path: str, data: bytes): - """ - Write data to a file - """ - if isinstance(data, str): - data = data.encode() - - agent.workspace.write(task_id=task_id, path=file_path, data=data) - return await agent.db.create_artifact( - task_id=task_id, - file_name=file_path.split("/")[-1], - relative_path=file_path, - agent_created=True, - ) - - -@action( - name="read_file", - description="Read data from a file", - parameters=[ - { - "name": "file_path", - "description": "Path to the file", - "type": "string", - "required": True, - }, - ], - output_type="bytes", -) -async def read_file(agent, task_id: str, file_path: str) -> bytes: - """ - Read data from a file - """ - return agent.workspace.read(task_id=task_id, path=file_path) diff --git a/autogpts/forge/forge/actions/finish.py b/autogpts/forge/forge/actions/finish.py deleted file mode 100644 index d989de328f..0000000000 --- a/autogpts/forge/forge/actions/finish.py +++ /dev/null @@ -1,38 +0,0 @@ -from forge.sdk.forge_log import ForgeLogger - -from .registry import action - -logger = ForgeLogger(__name__) - - -@action( - name="finish", - description="Use this to shut down once you have accomplished all of your goals," - " or when there are insurmountable problems that make it impossible" - " for you to finish your task.", - parameters=[ - { - "name": "reason", - "description": "A summary to the user of how the goals were accomplished", - "type": "string", - "required": True, - } - ], - output_type="None", -) -async def finish( - agent, - task_id: str, - reason: str, -) -> str: - """ - A function that takes in a string and exits the program - - Parameters: - reason (str): A summary to the user of how the goals were accomplished. - Returns: - A result string from create chat completion. A list of suggestions to - improve the code. - """ - logger.info(reason, extra={"title": "Shutting down...\n"}) - return reason diff --git a/autogpts/forge/forge/actions/registry.py b/autogpts/forge/forge/actions/registry.py deleted file mode 100644 index 6f548543f3..0000000000 --- a/autogpts/forge/forge/actions/registry.py +++ /dev/null @@ -1,193 +0,0 @@ -import glob -import importlib -import inspect -import os -from typing import Any, Callable, List - -import pydantic - - -class ActionParameter(pydantic.BaseModel): - """ - This class represents a parameter for an action. - - Attributes: - name (str): The name of the parameter. - description (str): A brief description of what the parameter does. - type (str): The type of the parameter. - required (bool): A flag indicating whether the parameter is required or optional. - """ - - name: str - description: str - type: str - required: bool - - -class Action(pydantic.BaseModel): - """ - This class represents an action in the system. - - Attributes: - name (str): The name of the action. - description (str): A brief description of what the action does. - method (Callable): The method that implements the action. - parameters (List[ActionParameter]): A list of parameters that the action requires. - output_type (str): The type of the output that the action returns. - """ - - name: str - description: str - method: Callable - parameters: List[ActionParameter] - output_type: str - category: str | None = None - - def __call__(self, *args: Any, **kwds: Any) -> Any: - """ - This method allows the class instance to be called as a function. - - Args: - *args: Variable length argument list. - **kwds: Arbitrary keyword arguments. - - Returns: - Any: The result of the method call. - """ - return self.method(*args, **kwds) - - def __str__(self) -> str: - """ - This method returns a string representation of the class instance. - - Returns: - str: A string representation of the class instance. - """ - func_summary = f"{self.name}(" - for param in self.parameters: - func_summary += f"{param.name}: {param.type}, " - func_summary = func_summary[:-2] + ")" - func_summary += f" -> {self.output_type}. Usage: {self.description}," - return func_summary - - -def action( - name: str, description: str, parameters: List[ActionParameter], output_type: str -): - def decorator(func): - func_params = inspect.signature(func).parameters - param_names = set( - [ActionParameter.parse_obj(param).name for param in parameters] - ) - param_names.add("agent") - param_names.add("task_id") - func_param_names = set(func_params.keys()) - if param_names != func_param_names: - raise ValueError( - f"Mismatch in parameter names. Action Annotation includes {param_names}, but function actually takes {func_param_names} in function {func.__name__} signature" - ) - func.action = Action( - name=name, - description=description, - parameters=parameters, - method=func, - output_type=output_type, - ) - return func - - return decorator - - -class ActionRegister: - def __init__(self, agent) -> None: - self.abilities = {} - self.register_abilities() - self.agent = agent - - def register_abilities(self) -> None: - for action_path in glob.glob( - os.path.join(os.path.dirname(__file__), "**/*.py"), recursive=True - ): - if not os.path.basename(action_path) in [ - "__init__.py", - "registry.py", - ]: - action = os.path.relpath( - action_path, os.path.dirname(__file__) - ).replace("/", ".") - try: - module = importlib.import_module( - f".{action[:-3]}", package="forge.actions" - ) - for attr in dir(module): - func = getattr(module, attr) - if hasattr(func, "action"): - ab = func.action - - ab.category = ( - action.split(".")[0].lower().replace("_", " ") - if len(action.split(".")) > 1 - else "general" - ) - self.abilities[func.action.name] = func.action - except Exception as e: - print(f"Error occurred while registering abilities: {str(e)}") - - def list_abilities(self) -> List[Action]: - return self.abilities - - def list_abilities_for_prompt(self) -> List[str]: - return [str(action) for action in self.abilities.values()] - - def abilities_description(self) -> str: - abilities_by_category = {} - for action in self.abilities.values(): - if action.category not in abilities_by_category: - abilities_by_category[action.category] = [] - abilities_by_category[action.category].append(str(action)) - - abilities_description = "" - for category, abilities in abilities_by_category.items(): - if abilities_description != "": - abilities_description += "\n" - abilities_description += f"{category}:" - for action in abilities: - abilities_description += f" {action}" - - return abilities_description - - async def run_action( - self, task_id: str, action_name: str, *args: Any, **kwds: Any - ) -> Any: - """ - This method runs a specified action with the provided arguments and keyword arguments. - - The agent is passed as the first argument to the action. This allows the action to access and manipulate - the agent's state as needed. - - Args: - task_id (str): The ID of the task that the action is being run for. - action_name (str): The name of the action to run. - *args: Variable length argument list. - **kwds: Arbitrary keyword arguments. - - Returns: - Any: The result of the action execution. - - Raises: - Exception: If there is an error in running the action. - """ - try: - action = self.abilities[action_name] - return await action(self.agent, task_id, *args, **kwds) - except Exception: - raise - - -if __name__ == "__main__": - import sys - - sys.path.append("/Users/swifty/dev/forge/forge") - register = ActionRegister(agent=None) - print(register.abilities_description()) - print(register.run_action("abc", "list_files", "/Users/swifty/dev/forge/forge")) diff --git a/autogpts/forge/forge/actions/web/web_search.py b/autogpts/forge/forge/actions/web/web_search.py deleted file mode 100644 index b143fed97b..0000000000 --- a/autogpts/forge/forge/actions/web/web_search.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -import json -import time - -from duckduckgo_search import DDGS - -from ..registry import action - -DUCKDUCKGO_MAX_ATTEMPTS = 3 - - -@action( - name="web_search", - description="Searches the web", - parameters=[ - { - "name": "query", - "description": "The search query", - "type": "string", - "required": True, - } - ], - output_type="list[str]", -) -async def web_search(agent, task_id: str, query: str) -> str: - """Return the results of a Google search - - Args: - query (str): The search query. - num_results (int): The number of results to return. - - Returns: - str: The results of the search. - """ - search_results = [] - attempts = 0 - num_results = 8 - - while attempts < DUCKDUCKGO_MAX_ATTEMPTS: - if not query: - return json.dumps(search_results) - - search_results = DDGS().text(query, max_results=num_results) - - if search_results: - break - - time.sleep(1) - attempts += 1 - - results = json.dumps(search_results, ensure_ascii=False, indent=4) - return safe_google_results(results) - - -def safe_google_results(results: str | list) -> str: - """ - Return the results of a Google search in a safe format. - - Args: - results (str | list): The search results. - - Returns: - str: The results of the search. - """ - if isinstance(results, list): - safe_message = json.dumps( - [result.encode("utf-8", "ignore").decode("utf-8") for result in results] - ) - else: - safe_message = results.encode("utf-8", "ignore").decode("utf-8") - return safe_message diff --git a/autogpts/forge/forge/actions/web/web_selenium.py b/autogpts/forge/forge/actions/web/web_selenium.py deleted file mode 100644 index 8de9aa34fc..0000000000 --- a/autogpts/forge/forge/actions/web/web_selenium.py +++ /dev/null @@ -1,366 +0,0 @@ -"""Commands for browsing a website""" - -from __future__ import annotations - -COMMAND_CATEGORY = "web_browse" -COMMAND_CATEGORY_TITLE = "Web Browsing" - -import functools -import logging -import re -from pathlib import Path -from sys import platform -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Type -from urllib.parse import urljoin, urlparse - -from bs4 import BeautifulSoup -from requests.compat import urljoin -from selenium.common.exceptions import WebDriverException -from selenium.webdriver.chrome.options import Options as ChromeOptions -from selenium.webdriver.chrome.service import Service as ChromeDriverService -from selenium.webdriver.chrome.webdriver import WebDriver as ChromeDriver -from selenium.webdriver.common.by import By -from selenium.webdriver.common.options import ArgOptions as BrowserOptions -from selenium.webdriver.edge.options import Options as EdgeOptions -from selenium.webdriver.edge.service import Service as EdgeDriverService -from selenium.webdriver.edge.webdriver import WebDriver as EdgeDriver -from selenium.webdriver.firefox.options import Options as FirefoxOptions -from selenium.webdriver.firefox.service import Service as GeckoDriverService -from selenium.webdriver.firefox.webdriver import WebDriver as FirefoxDriver -from selenium.webdriver.remote.webdriver import WebDriver -from selenium.webdriver.safari.options import Options as SafariOptions -from selenium.webdriver.safari.webdriver import WebDriver as SafariDriver -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.support.wait import WebDriverWait -from webdriver_manager.chrome import ChromeDriverManager -from webdriver_manager.firefox import GeckoDriverManager -from webdriver_manager.microsoft import EdgeChromiumDriverManager as EdgeDriverManager - -from forge.utils.exceptions import CommandExecutionError - -from ..registry import action - - -def extract_hyperlinks(soup: BeautifulSoup, base_url: str) -> list[tuple[str, str]]: - """Extract hyperlinks from a BeautifulSoup object - - Args: - soup (BeautifulSoup): The BeautifulSoup object - base_url (str): The base URL - - Returns: - List[Tuple[str, str]]: The extracted hyperlinks - """ - return [ - (link.text, urljoin(base_url, link["href"])) - for link in soup.find_all("a", href=True) - ] - - -def format_hyperlinks(hyperlinks: list[tuple[str, str]]) -> list[str]: - """Format hyperlinks to be displayed to the user - - Args: - hyperlinks (List[Tuple[str, str]]): The hyperlinks to format - - Returns: - List[str]: The formatted hyperlinks - """ - return [f"{link_text} ({link_url})" for link_text, link_url in hyperlinks] - - -def validate_url(func: Callable[..., Any]) -> Any: - """The method decorator validate_url is used to validate urls for any command that requires - a url as an argument""" - - @functools.wraps(func) - def wrapper(url: str, *args, **kwargs) -> Any: - """Check if the URL is valid using a basic check, urllib check, and local file check - - Args: - url (str): The URL to check - - Returns: - the result of the wrapped function - - Raises: - ValueError if the url fails any of the validation tests - """ - # Most basic check if the URL is valid: - if not re.match(r"^https?://", url): - raise ValueError("Invalid URL format") - if not is_valid_url(url): - raise ValueError("Missing Scheme or Network location") - # Restrict access to local files - if check_local_file_access(url): - raise ValueError("Access to local files is restricted") - # Check URL length - if len(url) > 2000: - raise ValueError("URL is too long") - - return func(sanitize_url(url), *args, **kwargs) - - return wrapper - - -def is_valid_url(url: str) -> bool: - """Check if the URL is valid - - Args: - url (str): The URL to check - - Returns: - bool: True if the URL is valid, False otherwise - """ - try: - result = urlparse(url) - return all([result.scheme, result.netloc]) - except ValueError: - return False - - -def sanitize_url(url: str) -> str: - """Sanitize the URL - - Args: - url (str): The URL to sanitize - - Returns: - str: The sanitized URL - """ - parsed_url = urlparse(url) - reconstructed_url = f"{parsed_url.path}{parsed_url.params}?{parsed_url.query}" - return urljoin(url, reconstructed_url) - - -def check_local_file_access(url: str) -> bool: - """Check if the URL is a local file - - Args: - url (str): The URL to check - - Returns: - bool: True if the URL is a local file, False otherwise - """ - local_prefixes = [ - "file:///", - "file://localhost/", - "file://localhost", - "http://localhost", - "http://localhost/", - "https://localhost", - "https://localhost/", - "http://2130706433", - "http://2130706433/", - "https://2130706433", - "https://2130706433/", - "http://127.0.0.1/", - "http://127.0.0.1", - "https://127.0.0.1/", - "https://127.0.0.1", - "https://0.0.0.0/", - "https://0.0.0.0", - "http://0.0.0.0/", - "http://0.0.0.0", - "http://0000", - "http://0000/", - "https://0000", - "https://0000/", - ] - return any(url.startswith(prefix) for prefix in local_prefixes) - - -logger = logging.getLogger(__name__) - -FILE_DIR = Path(__file__).parent.parent -TOKENS_TO_TRIGGER_SUMMARY = 50 -LINKS_TO_RETURN = 20 - - -class BrowsingError(CommandExecutionError): - """An error occurred while trying to browse the page""" - - -@action( - name="read_webpage", - description="Read a webpage, and extract specific information from it if a question is specified. If you are looking to extract specific information from the webpage, you should specify a question.", - parameters=[ - { - "name": "url", - "description": "The URL to visit", - "type": "string", - "required": True, - }, - { - "name": "question", - "description": "A question that you want to answer using the content of the webpage.", - "type": "string", - "required": False, - }, - ], - output_type="string", -) -@validate_url -async def read_webpage( - agent, task_id: str, url: str, question: str = "" -) -> Tuple(str, List[str]): - """Browse a website and return the answer and links to the user - - Args: - url (str): The url of the website to browse - question (str): The question to answer using the content of the webpage - - Returns: - str: The answer and links to the user and the webdriver - """ - driver = None - try: - driver = open_page_in_browser(url) - - text = scrape_text_with_selenium(driver) - links = scrape_links_with_selenium(driver, url) - - if not text: - return f"Website did not contain any text.\n\nLinks: {links}" - - # Limit links to LINKS_TO_RETURN - if len(links) > LINKS_TO_RETURN: - links = links[:LINKS_TO_RETURN] - return (text, links) - - except WebDriverException as e: - # These errors are often quite long and include lots of context. - # Just grab the first line. - msg = e.msg.split("\n")[0] - if "net::" in msg: - raise BrowsingError( - f"A networking error occurred while trying to load the page: " - + re.sub(r"^unknown error: ", "", msg) - ) - raise CommandExecutionError(msg) - finally: - if driver: - close_browser(driver) - - -def scrape_text_with_selenium(driver: WebDriver) -> str: - """Scrape text from a browser window using selenium - - Args: - driver (WebDriver): A driver object representing the browser window to scrape - - Returns: - str: the text scraped from the website - """ - - # Get the HTML content directly from the browser's DOM - page_source = driver.execute_script("return document.body.outerHTML;") - soup = BeautifulSoup(page_source, "html.parser") - - for script in soup(["script", "style"]): - script.extract() - - text = soup.get_text() - lines = (line.strip() for line in text.splitlines()) - chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) - text = "\n".join(chunk for chunk in chunks if chunk) - return text - - -def scrape_links_with_selenium(driver: WebDriver, base_url: str) -> list[str]: - """Scrape links from a website using selenium - - Args: - driver (WebDriver): A driver object representing the browser window to scrape - base_url (str): The base URL to use for resolving relative links - - Returns: - List[str]: The links scraped from the website - """ - page_source = driver.page_source - soup = BeautifulSoup(page_source, "html.parser") - - for script in soup(["script", "style"]): - script.extract() - - hyperlinks = extract_hyperlinks(soup, base_url) - - return format_hyperlinks(hyperlinks) - - -def open_page_in_browser(url: str) -> WebDriver: - """Open a browser window and load a web page using Selenium - - Params: - url (str): The URL of the page to load - - Returns: - driver (WebDriver): A driver object representing the browser window to scrape - """ - logging.getLogger("selenium").setLevel(logging.CRITICAL) - selenium_web_browser = "chrome" - selenium_headless = True - options_available: dict[str, Type[BrowserOptions]] = { - "chrome": ChromeOptions, - "edge": EdgeOptions, - "firefox": FirefoxOptions, - "safari": SafariOptions, - } - - options: BrowserOptions = options_available[selenium_web_browser]() - options.add_argument( - "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.49 Safari/537.36" - ) - - if selenium_web_browser == "firefox": - if selenium_headless: - options.headless = True - options.add_argument("--disable-gpu") - driver = FirefoxDriver( - service=GeckoDriverService(GeckoDriverManager().install()), options=options - ) - elif selenium_web_browser == "edge": - driver = EdgeDriver( - service=EdgeDriverService(EdgeDriverManager().install()), options=options - ) - elif selenium_web_browser == "safari": - # Requires a bit more setup on the users end - # See https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari - driver = SafariDriver(options=options) - else: - if platform == "linux" or platform == "linux2": - options.add_argument("--disable-dev-shm-usage") - options.add_argument("--remote-debugging-port=9222") - - options.add_argument("--no-sandbox") - if selenium_headless: - options.add_argument("--headless=new") - options.add_argument("--disable-gpu") - - chromium_driver_path = Path("/usr/bin/chromedriver") - - driver = ChromeDriver( - service=ChromeDriverService(str(chromium_driver_path)) - if chromium_driver_path.exists() - else ChromeDriverService(ChromeDriverManager().install()), - options=options, - ) - driver.get(url) - - WebDriverWait(driver, 10).until( - EC.presence_of_element_located((By.TAG_NAME, "body")) - ) - - return driver - - -def close_browser(driver: WebDriver) -> None: - """Close the browser - - Args: - driver (WebDriver): The webdriver to close - - Returns: - None - """ - driver.quit() diff --git a/autogpts/forge/forge/agent.py b/autogpts/forge/forge/agent.py deleted file mode 100644 index 7757c66802..0000000000 --- a/autogpts/forge/forge/agent.py +++ /dev/null @@ -1,146 +0,0 @@ -from forge.actions import ActionRegister -from forge.sdk import ( - Agent, - AgentDB, - ForgeLogger, - Step, - StepRequestBody, - Task, - TaskRequestBody, - Workspace, -) - -LOG = ForgeLogger(__name__) - - -class ForgeAgent(Agent): - """ - The goal of the Forge is to take care of the boilerplate code, so you can focus on - agent design. - - There is a great paper surveying the agent landscape: https://arxiv.org/abs/2308.11432 - Which I would highly recommend reading as it will help you understand the possabilities. - - Here is a summary of the key components of an agent: - - Anatomy of an agent: - - Profile - - Memory - - Planning - - Action - - Profile: - - Agents typically perform a task by assuming specific roles. For example, a teacher, - a coder, a planner etc. In using the profile in the llm prompt it has been shown to - improve the quality of the output. https://arxiv.org/abs/2305.14688 - - Additionally, based on the profile selected, the agent could be configured to use a - different llm. The possibilities are endless and the profile can be selected - dynamically based on the task at hand. - - Memory: - - Memory is critical for the agent to accumulate experiences, self-evolve, and behave - in a more consistent, reasonable, and effective manner. There are many approaches to - memory. However, some thoughts: there is long term and short term or working memory. - You may want different approaches for each. There has also been work exploring the - idea of memory reflection, which is the ability to assess its memories and re-evaluate - them. For example, condensing short term memories into long term memories. - - Planning: - - When humans face a complex task, they first break it down into simple subtasks and then - solve each subtask one by one. The planning module empowers LLM-based agents with the ability - to think and plan for solving complex tasks, which makes the agent more comprehensive, - powerful, and reliable. The two key methods to consider are: Planning with feedback and planning - without feedback. - - Action: - - Actions translate the agent's decisions into specific outcomes. For example, if the agent - decides to write a file, the action would be to write the file. There are many approaches you - could implement actions. - - The Forge has a basic module for each of these areas. However, you are free to implement your own. - This is just a starting point. - """ - - def __init__(self, database: AgentDB, workspace: Workspace): - """ - The database is used to store tasks, steps and artifact metadata. The workspace is used to - store artifacts. The workspace is a directory on the file system. - - Feel free to create subclasses of the database and workspace to implement your own storage - """ - super().__init__(database, workspace) - self.abilities = ActionRegister(self) - - async def create_task(self, task_request: TaskRequestBody) -> Task: - """ - The agent protocol, which is the core of the Forge, works by creating a task and then - executing steps for that task. This method is called when the agent is asked to create - a task. - - We are hooking into function to add a custom log message. Though you can do anything you - want here. - """ - task = await super().create_task(task_request) - LOG.info( - f"📦 Task created: {task.task_id} input: {task.input[:40]}{'...' if len(task.input) > 40 else ''}" - ) - return task - - async def execute_step(self, task_id: str, step_request: StepRequestBody) -> Step: - """ - For a tutorial on how to add your own logic please see the offical tutorial series: - https://aiedge.medium.com/autogpt-forge-e3de53cc58ec - - The agent protocol, which is the core of the Forge, works by creating a task and then - executing steps for that task. This method is called when the agent is asked to execute - a step. - - The task that is created contains an input string, for the benchmarks this is the task - the agent has been asked to solve and additional input, which is a dictionary and - could contain anything. - - If you want to get the task use: - - ``` - task = await self.db.get_task(task_id) - ``` - - The step request body is essentially the same as the task request and contains an input - string, for the benchmarks this is the task the agent has been asked to solve and - additional input, which is a dictionary and could contain anything. - - You need to implement logic that will take in this step input and output the completed step - as a step object. You can do everything in a single step or you can break it down into - multiple steps. Returning a request to continue in the step output, the user can then decide - if they want the agent to continue or not. - """ - # An example that - step = await self.db.create_step( - task_id=task_id, input=step_request, is_last=True - ) - - self.workspace.write(task_id=task_id, path="output.txt", data=b"Washington D.C") - - await self.db.create_artifact( - task_id=task_id, - step_id=step.step_id, - file_name="output.txt", - relative_path="", - agent_created=True, - ) - - step.output = "Washington D.C" - - LOG.info( - f"\t✅ Final Step completed: {step.step_id}. \n" - + f"Output should be placeholder text Washington D.C. You'll need to \n" - + f"modify execute_step to include LLM behavior. Follow the tutorial " - + f"if confused. " - ) - - return step diff --git a/autogpts/forge/forge/agent/__init__.py b/autogpts/forge/forge/agent/__init__.py deleted file mode 100644 index da65968a8f..0000000000 --- a/autogpts/forge/forge/agent/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from .base import AgentMeta, BaseAgent, BaseAgentConfiguration, BaseAgentSettings -from .components import ( - AgentComponent, - ComponentEndpointError, - ComponentSystemError, - EndpointPipelineError, -) -from .protocols import ( - AfterExecute, - AfterParse, - CommandProvider, - DirectiveProvider, - ExecutionFailure, - MessageProvider, -) diff --git a/autogpts/forge/forge/app.py b/autogpts/forge/forge/app.py deleted file mode 100644 index e04edd756c..0000000000 --- a/autogpts/forge/forge/app.py +++ /dev/null @@ -1,13 +0,0 @@ -import os - -from forge.agent import ForgeAgent -from forge.sdk import LocalWorkspace - -from .db import ForgeDatabase - -database_name = os.getenv("DATABASE_STRING") -workspace = LocalWorkspace(os.getenv("AGENT_WORKSPACE")) -database = ForgeDatabase(database_name, debug_enabled=False) -agent = ForgeAgent(database=database, workspace=workspace) - -app = agent.get_agent_app() diff --git a/autogpts/forge/forge/command/__init__.py b/autogpts/forge/forge/command/__init__.py deleted file mode 100644 index 752c918513..0000000000 --- a/autogpts/forge/forge/command/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .command import Command, CommandOutput, CommandParameter -from .decorator import command -from .parameter import CommandParameter diff --git a/autogpts/forge/forge/components/code_executor/__init__.py b/autogpts/forge/forge/components/code_executor/__init__.py deleted file mode 100644 index 33cf11f480..0000000000 --- a/autogpts/forge/forge/components/code_executor/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .code_executor import ( - ALLOWLIST_CONTROL, - DENYLIST_CONTROL, - CodeExecutorComponent, - is_docker_available, - we_are_running_in_a_docker_container, -) diff --git a/autogpts/forge/forge/components/system/system.py b/autogpts/forge/forge/components/system/system.py deleted file mode 100644 index 9b72aecfe1..0000000000 --- a/autogpts/forge/forge/components/system/system.py +++ /dev/null @@ -1,54 +0,0 @@ -import logging -import time -from typing import Iterator - -from forge.agent.protocols import CommandProvider, DirectiveProvider, MessageProvider -from forge.command import Command, command -from forge.config.ai_profile import AIProfile -from forge.config.config import Config -from forge.llm.providers import ChatMessage -from forge.models.json_schema import JSONSchema -from forge.utils.const import FINISH_COMMAND -from forge.utils.exceptions import AgentFinished - -logger = logging.getLogger(__name__) - - -class SystemComponent(DirectiveProvider, MessageProvider, CommandProvider): - """Component for system messages and commands.""" - - def __init__(self, config: Config, profile: AIProfile): - self.legacy_config = config - self.profile = profile - - def get_constraints(self) -> Iterator[str]: - if self.profile.api_budget > 0.0: - yield ( - f"It takes money to let you run. " - f"Your API budget is ${self.profile.api_budget:.3f}" - ) - - def get_messages(self) -> Iterator[ChatMessage]: - # Clock - yield ChatMessage.system( - f"## Clock\nThe current time and date is {time.strftime('%c')}" - ) - - def get_commands(self) -> Iterator[Command]: - yield self.finish - - @command( - names=[FINISH_COMMAND], - parameters={ - "reason": JSONSchema( - type=JSONSchema.Type.STRING, - description="A summary to the user of how the goals were accomplished", - required=True, - ), - }, - ) - def finish(self, reason: str): - """Use this to shut down once you have completed your task, - or when there are insurmountable problems that make it impossible - for you to finish your task.""" - raise AgentFinished(reason) diff --git a/autogpts/forge/forge/config/ai_directives.py b/autogpts/forge/forge/config/ai_directives.py deleted file mode 100644 index 9e40e29a12..0000000000 --- a/autogpts/forge/forge/config/ai_directives.py +++ /dev/null @@ -1,49 +0,0 @@ -import logging -from pathlib import Path - -import yaml -from pydantic import BaseModel, Field - -from forge.utils.yaml_validator import validate_yaml_file - -logger = logging.getLogger(__name__) - - -class AIDirectives(BaseModel): - """An object that contains the basic directives for the AI prompt. - - Attributes: - constraints (list): A list of constraints that the AI should adhere to. - resources (list): A list of resources that the AI can utilize. - best_practices (list): A list of best practices that the AI should follow. - """ - - resources: list[str] = Field(default_factory=list) - constraints: list[str] = Field(default_factory=list) - best_practices: list[str] = Field(default_factory=list) - - @staticmethod - def from_file(prompt_settings_file: Path) -> "AIDirectives": - from forge.logging.helpers import request_user_double_check - - (validated, message) = validate_yaml_file(prompt_settings_file) - if not validated: - logger.error(message, extra={"title": "FAILED FILE VALIDATION"}) - request_user_double_check() - raise RuntimeError(f"File validation failed: {message}") - - with open(prompt_settings_file, encoding="utf-8") as file: - config_params = yaml.load(file, Loader=yaml.SafeLoader) - - return AIDirectives( - constraints=config_params.get("constraints", []), - resources=config_params.get("resources", []), - best_practices=config_params.get("best_practices", []), - ) - - def __add__(self, other: "AIDirectives") -> "AIDirectives": - return AIDirectives( - resources=self.resources + other.resources, - constraints=self.constraints + other.constraints, - best_practices=self.best_practices + other.best_practices, - ).copy(deep=True) diff --git a/autogpts/forge/forge/config/ai_profile.py b/autogpts/forge/forge/config/ai_profile.py deleted file mode 100644 index 3f0043c798..0000000000 --- a/autogpts/forge/forge/config/ai_profile.py +++ /dev/null @@ -1,68 +0,0 @@ -from pathlib import Path - -import yaml -from pydantic import BaseModel, Field - - -class AIProfile(BaseModel): - """ - Object to hold the AI's personality. - - Attributes: - ai_name (str): The name of the AI. - ai_role (str): The description of the AI's role. - ai_goals (list): The list of objectives the AI is supposed to complete. - api_budget (float): The maximum dollar value for API calls (0.0 means infinite) - """ - - ai_name: str = "" - ai_role: str = "" - ai_goals: list[str] = Field(default_factory=list[str]) - api_budget: float = 0.0 - - @staticmethod - def load(ai_settings_file: str | Path) -> "AIProfile": - """ - Returns class object with parameters (ai_name, ai_role, ai_goals, api_budget) - loaded from yaml file if it exists, else returns class with no parameters. - - Parameters: - ai_settings_file (Path): The path to the config yaml file. - - Returns: - cls (object): An instance of given cls object - """ - - try: - with open(ai_settings_file, encoding="utf-8") as file: - config_params = yaml.load(file, Loader=yaml.SafeLoader) or {} - except FileNotFoundError: - config_params = {} - - ai_name = config_params.get("ai_name", "") - ai_role = config_params.get("ai_role", "") - ai_goals = [ - str(goal).strip("{}").replace("'", "").replace('"', "") - if isinstance(goal, dict) - else str(goal) - for goal in config_params.get("ai_goals", []) - ] - api_budget = config_params.get("api_budget", 0.0) - - return AIProfile( - ai_name=ai_name, ai_role=ai_role, ai_goals=ai_goals, api_budget=api_budget - ) - - def save(self, ai_settings_file: str | Path) -> None: - """ - Saves the class parameters to the specified file yaml file path as a yaml file. - - Parameters: - ai_settings_file (Path): The path to the config yaml file. - - Returns: - None - """ - - with open(ai_settings_file, "w", encoding="utf-8") as file: - yaml.dump(self.dict(), file, allow_unicode=True) diff --git a/autogpts/forge/forge/db.py b/autogpts/forge/forge/db.py deleted file mode 100644 index cff096379a..0000000000 --- a/autogpts/forge/forge/db.py +++ /dev/null @@ -1,143 +0,0 @@ -import datetime -import uuid - -from sqlalchemy import Column, DateTime, String -from sqlalchemy.exc import SQLAlchemyError - -from .sdk import AgentDB, Base, ForgeLogger, NotFoundError - -LOG = ForgeLogger(__name__) - - -class ChatModel(Base): - __tablename__ = "chat" - msg_id = Column(String, primary_key=True, index=True) - task_id = Column(String) - role = Column(String) - content = Column(String) - created_at = Column(DateTime, default=datetime.datetime.utcnow) - modified_at = Column( - DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow - ) - - -class ActionModel(Base): - __tablename__ = "action" - action_id = Column(String, primary_key=True, index=True) - task_id = Column(String) - name = Column(String) - args = Column(String) - created_at = Column(DateTime, default=datetime.datetime.utcnow) - modified_at = Column( - DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow - ) - - -class ForgeDatabase(AgentDB): - async def add_chat_history(self, task_id, messages): - for message in messages: - await self.add_chat_message(task_id, message["role"], message["content"]) - - async def add_chat_message(self, task_id, role, content): - if self.debug_enabled: - LOG.debug("Creating new task") - try: - with self.Session() as session: - mew_msg = ChatModel( - msg_id=str(uuid.uuid4()), - task_id=task_id, - role=role, - content=content, - ) - session.add(mew_msg) - session.commit() - session.refresh(mew_msg) - if self.debug_enabled: - LOG.debug( - f"Created new Chat message with task_id: {mew_msg.msg_id}" - ) - return mew_msg - except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while creating task: {e}") - raise - except NotFoundError as e: - raise - except Exception as e: - LOG.error(f"Unexpected error while creating task: {e}") - raise - - async def get_chat_history(self, task_id): - if self.debug_enabled: - LOG.debug(f"Getting chat history with task_id: {task_id}") - try: - with self.Session() as session: - if messages := ( - session.query(ChatModel) - .filter(ChatModel.task_id == task_id) - .order_by(ChatModel.created_at) - .all() - ): - return [{"role": m.role, "content": m.content} for m in messages] - - else: - LOG.error(f"Chat history not found with task_id: {task_id}") - raise NotFoundError("Chat history not found") - except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting chat history: {e}") - raise - except NotFoundError as e: - raise - except Exception as e: - LOG.error(f"Unexpected error while getting chat history: {e}") - raise - - async def create_action(self, task_id, name, args): - try: - with self.Session() as session: - new_action = ActionModel( - action_id=str(uuid.uuid4()), - task_id=task_id, - name=name, - args=str(args), - ) - session.add(new_action) - session.commit() - session.refresh(new_action) - if self.debug_enabled: - LOG.debug( - f"Created new Action with task_id: {new_action.action_id}" - ) - return new_action - except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while creating action: {e}") - raise - except NotFoundError as e: - raise - except Exception as e: - LOG.error(f"Unexpected error while creating action: {e}") - raise - - async def get_action_history(self, task_id): - if self.debug_enabled: - LOG.debug(f"Getting action history with task_id: {task_id}") - try: - with self.Session() as session: - if actions := ( - session.query(ActionModel) - .filter(ActionModel.task_id == task_id) - .order_by(ActionModel.created_at) - .all() - ): - return [{"name": a.name, "args": a.args} for a in actions] - - else: - LOG.error(f"Action history not found with task_id: {task_id}") - raise NotFoundError("Action history not found") - except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting action history: {e}") - raise - except NotFoundError as e: - raise - except Exception as e: - LOG.error(f"Unexpected error while getting action history: {e}") - raise diff --git a/autogpts/forge/forge/json/__init__.py b/autogpts/forge/forge/json/__init__.py deleted file mode 100644 index 726072d03d..0000000000 --- a/autogpts/forge/forge/json/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .parsing import extract_dict_from_json, extract_list_from_json, json_loads diff --git a/autogpts/forge/forge/llm.py b/autogpts/forge/forge/llm.py deleted file mode 100644 index 4fe850052d..0000000000 --- a/autogpts/forge/forge/llm.py +++ /dev/null @@ -1,61 +0,0 @@ -from pathlib import Path - -from litellm import AuthenticationError, InvalidRequestError, ModelResponse, acompletion -from openai import OpenAI -from openai.types import CreateEmbeddingResponse -from openai.types.audio import Transcription -from tenacity import retry, stop_after_attempt, wait_random_exponential - -from .sdk.forge_log import ForgeLogger - -LOG = ForgeLogger(__name__) - - -@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3)) -async def chat_completion_request(model, messages, **kwargs) -> ModelResponse: - """Generate a response to a list of messages using OpenAI's API""" - try: - kwargs["model"] = model - kwargs["messages"] = messages - - resp = await acompletion(**kwargs) - return resp - except AuthenticationError as e: - LOG.exception("Authentication Error") - raise - except InvalidRequestError as e: - LOG.exception("Invalid Request Error") - raise - except Exception as e: - LOG.error("Unable to generate ChatCompletion response") - LOG.error(f"Exception: {e}") - raise - - -@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3)) -async def create_embedding_request( - messages, model="text-embedding-ada-002" -) -> CreateEmbeddingResponse: - """Generate an embedding for a list of messages using OpenAI's API""" - try: - return OpenAI().embeddings.create( - input=[f"{m['role']}: {m['content']}" for m in messages], - model=model, - ) - except Exception as e: - LOG.error("Unable to generate ChatCompletion response") - LOG.error(f"Exception: {e}") - raise - - -@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3)) -async def transcribe_audio(audio_file: Path) -> Transcription: - """Transcribe an audio file using OpenAI's API""" - try: - return OpenAI().audio.transcriptions.create( - model="whisper-1", file=audio_file.open(mode="rb") - ) - except Exception as e: - LOG.error("Unable to generate ChatCompletion response") - LOG.error(f"Exception: {e}") - raise diff --git a/autogpts/forge/forge/llm/prompting/prompt.py b/autogpts/forge/forge/llm/prompting/prompt.py deleted file mode 100644 index c9076aa9eb..0000000000 --- a/autogpts/forge/forge/llm/prompting/prompt.py +++ /dev/null @@ -1,5 +0,0 @@ -DEFAULT_TRIGGERING_PROMPT = ( - "Determine exactly one command to use next based on the given goals " - "and the progress you have made so far, " - "and respond using the JSON schema specified previously:" -) diff --git a/autogpts/forge/forge/logging/helpers.py b/autogpts/forge/forge/logging/helpers.py deleted file mode 100644 index d81f01d672..0000000000 --- a/autogpts/forge/forge/logging/helpers.py +++ /dev/null @@ -1,66 +0,0 @@ -import logging -from typing import Any, Optional - -from colorama import Fore - -from .config import SPEECH_OUTPUT_LOGGER, USER_FRIENDLY_OUTPUT_LOGGER - - -def user_friendly_output( - message: str, - level: int = logging.INFO, - title: str = "", - title_color: str = "", - preserve_message_color: bool = False, -) -> None: - """Outputs a message to the user in a user-friendly way. - - This function outputs on up to two channels: - 1. The console, in typewriter style - 2. Text To Speech, if configured - """ - logger = logging.getLogger(USER_FRIENDLY_OUTPUT_LOGGER) - - logger.log( - level, - message, - extra={ - "title": title, - "title_color": title_color, - "preserve_color": preserve_message_color, - }, - ) - - -def print_attribute( - title: str, value: Any, title_color: str = Fore.GREEN, value_color: str = "" -) -> None: - logger = logging.getLogger() - logger.info( - str(value), - extra={ - "title": f"{title.rstrip(':')}:", - "title_color": title_color, - "color": value_color, - }, - ) - - -def request_user_double_check(additionalText: Optional[str] = None) -> None: - if not additionalText: - additionalText = ( - "Please ensure you've setup and configured everything correctly. " - "Read https://docs.agpt.co/autogpt/setup/ to double check. " - "You can also create a github issue or join the discord and ask there!" - ) - - user_friendly_output( - additionalText, - level=logging.WARN, - title="DOUBLE CHECK CONFIGURATION", - preserve_message_color=True, - ) - - -def speak(message: str, level: int = logging.INFO) -> None: - logging.getLogger(SPEECH_OUTPUT_LOGGER).log(level, message) diff --git a/autogpts/forge/forge/logging/utils.py b/autogpts/forge/forge/logging/utils.py deleted file mode 100644 index d9f39af309..0000000000 --- a/autogpts/forge/forge/logging/utils.py +++ /dev/null @@ -1,9 +0,0 @@ -import re - - -def remove_color_codes(s: str) -> str: - return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", s) - - -def fmt_kwargs(kwargs: dict) -> str: - return ", ".join(f"{n}={repr(v)}" for n, v in kwargs.items()) diff --git a/autogpts/forge/forge/memory/__init__.py b/autogpts/forge/forge/memory/__init__.py deleted file mode 100644 index b0182da70b..0000000000 --- a/autogpts/forge/forge/memory/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .chroma_memstore import ChromaMemStore -from .memstore import MemStore diff --git a/autogpts/forge/forge/memory/chroma_memstore.py b/autogpts/forge/forge/memory/chroma_memstore.py deleted file mode 100644 index 7764f1ab91..0000000000 --- a/autogpts/forge/forge/memory/chroma_memstore.py +++ /dev/null @@ -1,161 +0,0 @@ -import hashlib - -import chromadb -from chromadb.config import Settings - -from .memstore import MemStore - - -class ChromaMemStore: - """ - A class used to represent a Memory Store - """ - - def __init__(self, store_path: str): - """ - Initialize the MemStore with a given store path. - - Args: - store_path (str): The path to the store. - """ - self.client = chromadb.PersistentClient( - path=store_path, settings=Settings(anonymized_telemetry=False) - ) - - def add(self, task_id: str, document: str, metadatas: dict) -> None: - """ - Add a document to the MemStore. - - Args: - task_id (str): The ID of the task. - document (str): The document to be added. - metadatas (dict): The metadata of the document. - """ - doc_id = hashlib.sha256(document.encode()).hexdigest()[:20] - collection = self.client.get_or_create_collection(task_id) - collection.add(documents=[document], metadatas=[metadatas], ids=[doc_id]) - - def query( - self, - task_id: str, - query: str, - filters: dict = None, - document_search: dict = None, - ) -> dict: - """ - Query the MemStore. - - Args: - task_id (str): The ID of the task. - query (str): The query string. - filters (dict, optional): The filters to be applied. Defaults to None. - search_string (str, optional): The search string. Defaults to None. - - Returns: - dict: The query results. - """ - collection = self.client.get_or_create_collection(task_id) - - kwargs = { - "query_texts": [query], - "n_results": 10, - } - - if filters: - kwargs["where"] = filters - - if document_search: - kwargs["where_document"] = document_search - - return collection.query(**kwargs) - - def get(self, task_id: str, doc_ids: list = None, filters: dict = None) -> dict: - """ - Get documents from the MemStore. - - Args: - task_id (str): The ID of the task. - doc_ids (list, optional): The IDs of the documents to be retrieved. Defaults to None. - filters (dict, optional): The filters to be applied. Defaults to None. - - Returns: - dict: The retrieved documents. - """ - collection = self.client.get_or_create_collection(task_id) - kwargs = {} - if doc_ids: - kwargs["ids"] = doc_ids - if filters: - kwargs["where"] = filters - return collection.get(**kwargs) - - def update(self, task_id: str, doc_ids: list, documents: list, metadatas: list): - """ - Update documents in the MemStore. - - Args: - task_id (str): The ID of the task. - doc_ids (list): The IDs of the documents to be updated. - documents (list): The updated documents. - metadatas (list): The updated metadata. - """ - collection = self.client.get_or_create_collection(task_id) - collection.update(ids=doc_ids, documents=documents, metadatas=metadatas) - - def delete(self, task_id: str, doc_id: str): - """ - Delete a document from the MemStore. - - Args: - task_id (str): The ID of the task. - doc_id (str): The ID of the document to be deleted. - """ - collection = self.client.get_or_create_collection(task_id) - collection.delete(ids=[doc_id]) - - -if __name__ == "__main__": - print("#############################################") - # Initialize MemStore - mem = ChromaMemStore(".agent_mem_store") - - # Test add function - task_id = "test_task" - document = "This is a another new test document." - metadatas = {"metadata": "test_metadata"} - mem.add(task_id, document, metadatas) - - task_id = "test_task" - document = "The quick brown fox jumps over the lazy dog." - metadatas = {"metadata": "test_metadata"} - mem.add(task_id, document, metadatas) - - task_id = "test_task" - document = "AI is a new technology that will change the world." - metadatas = {"timestamp": 1623936000} - mem.add(task_id, document, metadatas) - - doc_id = hashlib.sha256(document.encode()).hexdigest()[:20] - # Test query function - query = "test" - filters = {"metadata": {"$eq": "test"}} - search_string = {"$contains": "test"} - doc_ids = [doc_id] - documents = ["This is an updated test document."] - updated_metadatas = {"metadata": "updated_test_metadata"} - - print("Query:") - print(mem.query(task_id, query)) - - # Test get function - print("Get:") - - print(mem.get(task_id)) - - # Test update function - print("Update:") - print(mem.update(task_id, doc_ids, documents, updated_metadatas)) - - print("Delete:") - # Test delete function - print(mem.delete(task_id, doc_ids[0])) diff --git a/autogpts/forge/forge/memory/memstore.py b/autogpts/forge/forge/memory/memstore.py deleted file mode 100644 index 7ab9aae50b..0000000000 --- a/autogpts/forge/forge/memory/memstore.py +++ /dev/null @@ -1,151 +0,0 @@ -import abc -import hashlib - -import chromadb -from chromadb.config import Settings - - -class MemStore(abc.ABC): - """ - An abstract class that represents a Memory Store - """ - - @abc.abstractmethod - def __init__(self, store_path: str): - """ - Initialize the MemStore with a given store path. - - Args: - store_path (str): The path to the store. - """ - pass - - @abc.abstractmethod - def add_task_memory(self, task_id: str, document: str, metadatas: dict) -> None: - """ - Add a document to the current tasks MemStore. - This function calls the base version with the task_id as the collection_name. - - Args: - task_id (str): The ID of the task. - document (str): The document to be added. - metadatas (dict): The metadata of the document. - """ - self.add(collection_name=task_id, document=document, metadatas=metadatas) - - @abc.abstractmethod - def query_task_memory( - self, - task_id: str, - query: str, - filters: dict = None, - document_search: dict = None, - ) -> dict: - """ - Query the current tasks MemStore. - This function calls the base version with the task_id as the collection_name. - - Args: - task_id (str): The ID of the task. - query (str): The query string. - filters (dict, optional): The filters to be applied. Defaults to None. - document_search (dict, optional): The search string. Defaults to None. - - Returns: - dict: The query results. - """ - return self.query( - collection_name=task_id, - query=query, - filters=filters, - document_search=document_search, - ) - - @abc.abstractmethod - def get_task_memory( - self, task_id: str, doc_ids: list = None, filters: dict = None - ) -> dict: - """ - Get documents from the current tasks MemStore. - This function calls the base version with the task_id as the collection_name. - - Args: - task_id (str): The ID of the task. - doc_ids (list, optional): The IDs of the documents to be retrieved. Defaults to None. - filters (dict, optional): The filters to be applied. Defaults to None. - - Returns: - dict: The retrieved documents. - """ - return self.get(collection_name=task_id, doc_ids=doc_ids, filters=filters) - - @abc.abstractmethod - def update_task_memory( - self, task_id: str, doc_ids: list, documents: list, metadatas: list - ): - """ - Update documents in the current tasks MemStore. - This function calls the base version with the task_id as the collection_name. - - Args: - task_id (str): The ID of the task. - doc_ids (list): The IDs of the documents to be updated. - documents (list): The updated documents. - metadatas (list): The updated metadata. - """ - self.update( - collection_name=task_id, - doc_ids=doc_ids, - documents=documents, - metadatas=metadatas, - ) - - @abc.abstractmethod - def delete_task_memory(self, task_id: str, doc_id: str): - """ - Delete a document from the current tasks MemStore. - This function calls the base version with the task_id as the collection_name. - - Args: - task_id (str): The ID of the task. - doc_id (str): The ID of the document to be deleted. - """ - self.delete(collection_name=task_id, doc_id=doc_id) - - @abc.abstractmethod - def add(self, collection_name: str, document: str, metadatas: dict) -> None: - """ - Add a document to the current collection's MemStore. - - Args: - collection_name (str): The name of the collection. - document (str): The document to be added. - metadatas (dict): The metadata of the document. - """ - pass - - @abc.abstractmethod - def query( - self, - collection_name: str, - query: str, - filters: dict = None, - document_search: dict = None, - ) -> dict: - pass - - @abc.abstractmethod - def get( - self, collection_name: str, doc_ids: list = None, filters: dict = None - ) -> dict: - pass - - @abc.abstractmethod - def update( - self, collection_name: str, doc_ids: list, documents: list, metadatas: list - ): - pass - - @abc.abstractmethod - def delete(self, collection_name: str, doc_id: str): - pass diff --git a/autogpts/forge/forge/memory/memstore_test.py b/autogpts/forge/forge/memory/memstore_test.py deleted file mode 100644 index 200dc63d97..0000000000 --- a/autogpts/forge/forge/memory/memstore_test.py +++ /dev/null @@ -1,58 +0,0 @@ -import hashlib -import shutil - -import pytest - -from forge.memory.chroma_memstore import ChromaMemStore - - -@pytest.fixture -def memstore(): - mem = ChromaMemStore(".test_mem_store") - yield mem - shutil.rmtree(".test_mem_store") - - -def test_add(memstore): - task_id = "test_task" - document = "This is a test document." - metadatas = {"metadata": "test_metadata"} - memstore.add(task_id, document, metadatas) - doc_id = hashlib.sha256(document.encode()).hexdigest()[:20] - assert memstore.client.get_or_create_collection(task_id).count() == 1 - - -def test_query(memstore): - task_id = "test_task" - document = "This is a test document." - metadatas = {"metadata": "test_metadata"} - memstore.add(task_id, document, metadatas) - query = "test" - assert len(memstore.query(task_id, query)["documents"]) == 1 - - -def test_update(memstore): - task_id = "test_task" - document = "This is a test document." - metadatas = {"metadata": "test_metadata"} - memstore.add(task_id, document, metadatas) - doc_id = hashlib.sha256(document.encode()).hexdigest()[:20] - updated_document = "This is an updated test document." - updated_metadatas = {"metadata": "updated_test_metadata"} - memstore.update(task_id, [doc_id], [updated_document], [updated_metadatas]) - assert memstore.get(task_id, [doc_id]) == { - "documents": [updated_document], - "metadatas": [updated_metadatas], - "embeddings": None, - "ids": [doc_id], - } - - -def test_delete(memstore): - task_id = "test_task" - document = "This is a test document." - metadatas = {"metadata": "test_metadata"} - memstore.add(task_id, document, metadatas) - doc_id = hashlib.sha256(document.encode()).hexdigest()[:20] - memstore.delete(task_id, doc_id) - assert memstore.client.get_or_create_collection(task_id).count() == 0 diff --git a/autogpts/forge/forge/sdk/__init__.py b/autogpts/forge/forge/sdk/__init__.py deleted file mode 100644 index c3db17e31f..0000000000 --- a/autogpts/forge/forge/sdk/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -The Forge SDK. This is the core of the Forge. It contains the agent protocol, which is the -core of the Forge. -""" -from forge.utils.exceptions import ( - AccessDeniedError, - AgentException, - AgentFinished, - AgentTerminated, - CodeExecutionError, - CommandExecutionError, - ConfigurationError, - InvalidAgentResponseError, - InvalidArgumentError, - NotFoundError, - OperationNotAllowedError, - TooMuchOutputError, - UnknownCommandError, - get_detailed_traceback, - get_exception_message, -) - -from .agent import Agent -from .db import AgentDB, Base -from .forge_log import ForgeLogger -from .model import ( - Artifact, - ArtifactUpload, - Pagination, - Status, - Step, - StepOutput, - StepRequestBody, - Task, - TaskArtifactsListResponse, - TaskListResponse, - TaskRequestBody, - TaskStepsListResponse, -) -from .prompting import PromptEngine -from .workspace import LocalWorkspace, Workspace diff --git a/autogpts/forge/forge/sdk/agent_test.py b/autogpts/forge/forge/sdk/agent_test.py deleted file mode 100644 index d2d23abbee..0000000000 --- a/autogpts/forge/forge/sdk/agent_test.py +++ /dev/null @@ -1,107 +0,0 @@ -import pytest - -from .agent import Agent -from .db import AgentDB -from .model import StepRequestBody, Task, TaskListResponse, TaskRequestBody -from .workspace import LocalWorkspace - - -@pytest.fixture -def agent(): - db = AgentDB("sqlite:///test.db") - workspace = LocalWorkspace("./test_workspace") - return Agent(db, workspace) - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_create_task(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task: Task = await agent.create_task(task_request) - assert task.input == "test_input" - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_list_tasks(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - tasks = await agent.list_tasks() - assert isinstance(tasks, TaskListResponse) - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_get_task(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - retrieved_task = await agent.get_task(task.task_id) - assert retrieved_task.task_id == task.task_id - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_create_and_execute_step(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - step_request = StepRequestBody( - input="step_input", additional_input={"input": "additional_test_input"} - ) - step = await agent.create_and_execute_step(task.task_id, step_request) - assert step.input == "step_input" - assert step.additional_input == {"input": "additional_test_input"} - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_get_step(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - step_request = StepRequestBody( - input="step_input", additional_input={"input": "additional_test_input"} - ) - step = await agent.create_and_execute_step(task.task_id, step_request) - retrieved_step = await agent.get_step(task.task_id, step.step_id) - assert retrieved_step.step_id == step.step_id - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_list_artifacts(agent): - artifacts = await agent.list_artifacts() - assert isinstance(artifacts, list) - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_create_artifact(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - artifact_request = ArtifactRequestBody(file=None, uri="test_uri") - artifact = await agent.create_artifact(task.task_id, artifact_request) - assert artifact.uri == "test_uri" - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_get_artifact(agent): - task_request = TaskRequestBody( - input="test_input", additional_input={"input": "additional_test_input"} - ) - task = await agent.create_task(task_request) - artifact_request = ArtifactRequestBody(file=None, uri="test_uri") - artifact = await agent.create_artifact(task.task_id, artifact_request) - retrieved_artifact = await agent.get_artifact(task.task_id, artifact.artifact_id) - assert retrieved_artifact.artifact_id == artifact.artifact_id diff --git a/autogpts/forge/forge/sdk/forge_log.py b/autogpts/forge/forge/sdk/forge_log.py deleted file mode 100644 index ea607a2880..0000000000 --- a/autogpts/forge/forge/sdk/forge_log.py +++ /dev/null @@ -1,203 +0,0 @@ -import json -import logging -import logging.config -import logging.handlers -import os -import queue - -JSON_LOGGING = os.environ.get("JSON_LOGGING", "false").lower() == "true" - -CHAT = 29 -logging.addLevelName(CHAT, "CHAT") - -RESET_SEQ: str = "\033[0m" -COLOR_SEQ: str = "\033[1;%dm" -BOLD_SEQ: str = "\033[1m" -UNDERLINE_SEQ: str = "\033[04m" - -ORANGE: str = "\033[33m" -YELLOW: str = "\033[93m" -WHITE: str = "\33[37m" -BLUE: str = "\033[34m" -LIGHT_BLUE: str = "\033[94m" -RED: str = "\033[91m" -GREY: str = "\33[90m" -GREEN: str = "\033[92m" - -EMOJIS: dict[str, str] = { - "DEBUG": "🐛", - "INFO": "📝", - "CHAT": "💬", - "WARNING": "⚠️", - "ERROR": "❌", - "CRITICAL": "💥", -} - -KEYWORD_COLORS: dict[str, str] = { - "DEBUG": WHITE, - "INFO": LIGHT_BLUE, - "CHAT": GREEN, - "WARNING": YELLOW, - "ERROR": ORANGE, - "CRITICAL": RED, -} - - -class JsonFormatter(logging.Formatter): - def format(self, record): - return json.dumps(record.__dict__) - - -def formatter_message(message: str, use_color: bool = True) -> str: - """ - Syntax highlight certain keywords - """ - if use_color: - message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ) - else: - message = message.replace("$RESET", "").replace("$BOLD", "") - return message - - -def format_word( - message: str, word: str, color_seq: str, bold: bool = False, underline: bool = False -) -> str: - """ - Surround the fiven word with a sequence - """ - replacer = color_seq + word + RESET_SEQ - if underline: - replacer = UNDERLINE_SEQ + replacer - if bold: - replacer = BOLD_SEQ + replacer - return message.replace(word, replacer) - - -class ConsoleFormatter(logging.Formatter): - """ - This Formatted simply colors in the levelname i.e 'INFO', 'DEBUG' - """ - - def __init__( - self, fmt: str, datefmt: str = None, style: str = "%", use_color: bool = True - ): - super().__init__(fmt, datefmt, style) - self.use_color = use_color - - def format(self, record: logging.LogRecord) -> str: - """ - Format and highlight certain keywords - """ - rec = record - levelname = rec.levelname - if self.use_color and levelname in KEYWORD_COLORS: - levelname_color = KEYWORD_COLORS[levelname] + levelname + RESET_SEQ - rec.levelname = levelname_color - rec.name = f"{GREY}{rec.name:<15}{RESET_SEQ}" - rec.msg = ( - KEYWORD_COLORS[levelname] + EMOJIS[levelname] + " " + rec.msg + RESET_SEQ - ) - return logging.Formatter.format(self, rec) - - -class ForgeLogger(logging.Logger): - """ - This adds extra logging functions such as logger.trade and also - sets the logger to use the custom formatter - """ - - CONSOLE_FORMAT: str = ( - "[%(asctime)s] [$BOLD%(name)-15s$RESET] [%(levelname)-8s]\t%(message)s" - ) - FORMAT: str = "%(asctime)s %(name)-15s %(levelname)-8s %(message)s" - COLOR_FORMAT: str = formatter_message(CONSOLE_FORMAT, True) - JSON_FORMAT: str = '{"time": "%(asctime)s", "name": "%(name)s", "level": "%(levelname)s", "message": "%(message)s"}' - - def __init__(self, name: str, logLevel: str = "DEBUG"): - logging.Logger.__init__(self, name, logLevel) - - # Queue Handler - queue_handler = logging.handlers.QueueHandler(queue.Queue(-1)) - json_formatter = logging.Formatter(self.JSON_FORMAT) - queue_handler.setFormatter(json_formatter) - self.addHandler(queue_handler) - - if JSON_LOGGING: - console_formatter = JsonFormatter() - else: - console_formatter = ConsoleFormatter(self.COLOR_FORMAT) - console = logging.StreamHandler() - console.setFormatter(console_formatter) - self.addHandler(console) - - def chat(self, role: str, openai_repsonse: dict, messages=None, *args, **kws): - """ - Parse the content, log the message and extract the usage into prometheus metrics - """ - role_emojis = { - "system": "🖥️", - "user": "👤", - "assistant": "🤖", - "function": "⚙️", - } - if self.isEnabledFor(CHAT): - if messages: - for message in messages: - self._log( - CHAT, - f"{role_emojis.get(message['role'], '🔵')}: {message['content']}", - ) - else: - response = json.loads(openai_repsonse) - - self._log( - CHAT, - f"{role_emojis.get(role, '🔵')}: {response['choices'][0]['message']['content']}", - ) - - -class QueueLogger(logging.Logger): - """ - Custom logger class with queue - """ - - def __init__(self, name: str, level: int = logging.NOTSET): - super().__init__(name, level) - queue_handler = logging.handlers.QueueHandler(queue.Queue(-1)) - self.addHandler(queue_handler) - - -logging_config: dict = dict( - version=1, - formatters={ - "console": { - "()": ConsoleFormatter, - "format": ForgeLogger.COLOR_FORMAT, - }, - }, - handlers={ - "h": { - "class": "logging.StreamHandler", - "formatter": "console", - "level": logging.INFO, - }, - }, - root={ - "handlers": ["h"], - "level": logging.INFO, - }, - loggers={ - "autogpt": { - "handlers": ["h"], - "level": logging.INFO, - "propagate": False, - }, - }, -) - - -def setup_logger(): - """ - Setup the logger with the specified format - """ - logging.config.dictConfig(logging_config) diff --git a/autogpts/forge/forge/sdk/prompting.py b/autogpts/forge/forge/sdk/prompting.py deleted file mode 100644 index badc004db6..0000000000 --- a/autogpts/forge/forge/sdk/prompting.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Relative to this file I will have a prompt directory its located ../prompts -In this directory there will be a techniques directory and a directory for each model - gpt-3.5-turbo gpt-4, llama-2-70B, code-llama-7B etc - -Each directory will have jinga2 templates for the prompts. -prompts in the model directories can use the techniques in the techniques directory. - -Write the code I'd need to load and populate the templates. - -I want the following functions: - -class PromptEngine: - - def __init__(self, model): - pass - - def load_prompt(model, prompt_name, prompt_ags) -> str: - pass -""" - -import glob -import os -from difflib import get_close_matches -from typing import List - -from jinja2 import Environment, FileSystemLoader - -from .forge_log import ForgeLogger - -LOG = ForgeLogger(__name__) - - -class PromptEngine: - """ - Class to handle loading and populating Jinja2 templates for prompts. - """ - - def __init__(self, model: str, debug_enabled: bool = False): - """ - Initialize the PromptEngine with the specified model. - - Args: - model (str): The model to use for loading prompts. - debug_enabled (bool): Enable or disable debug logging. - """ - self.model = model - self.debug_enabled = debug_enabled - if self.debug_enabled: - LOG.debug(f"Initializing PromptEngine for model: {model}") - - try: - # Get the list of all model directories - models_dir = os.path.abspath( - os.path.join(os.path.dirname(__file__), "../prompts") - ) - model_names = [ - os.path.basename(os.path.normpath(d)) - for d in glob.glob(os.path.join(models_dir, "*/")) - if os.path.isdir(d) and "techniques" not in d - ] - - self.model = self.get_closest_match(self.model, model_names) - - if self.debug_enabled: - LOG.debug(f"Using the closest match model for prompts: {self.model}") - - self.env = Environment(loader=FileSystemLoader(models_dir)) - except Exception as e: - LOG.error(f"Error initializing Environment: {e}") - raise - - @staticmethod - def get_closest_match(target: str, model_dirs: List[str]) -> str: - """ - Find the closest match to the target in the list of model directories. - - Args: - target (str): The target model. - model_dirs (list): The list of available model directories. - - Returns: - str: The closest match to the target. - """ - try: - matches = get_close_matches(target, model_dirs, n=1, cutoff=0.1) - if matches: - matches_str = ", ".join(matches) - LOG.debug(matches_str) - for m in matches: - LOG.info(m) - return matches[0] - except Exception as e: - LOG.error(f"Error finding closest match: {e}") - raise - - def load_prompt(self, template: str, **kwargs) -> str: - """ - Load and populate the specified template. - - Args: - template (str): The name of the template to load. - **kwargs: The arguments to populate the template with. - - Returns: - str: The populated template. - """ - try: - template = os.path.join(self.model, template) - if self.debug_enabled: - LOG.debug(f"Loading template: {template}") - template = self.env.get_template(f"{template}.j2") - if self.debug_enabled: - LOG.debug(f"Rendering template: {template} with args: {kwargs}") - return template.render(**kwargs) - except Exception as e: - LOG.error(f"Error loading or rendering template: {e}") - raise diff --git a/autogpts/forge/forge/sdk/routes/__init__.py b/autogpts/forge/forge/sdk/routes/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/autogpts/forge/forge/sdk/workspace.py b/autogpts/forge/forge/sdk/workspace.py deleted file mode 100644 index ff7c524277..0000000000 --- a/autogpts/forge/forge/sdk/workspace.py +++ /dev/null @@ -1,133 +0,0 @@ -import abc -import os -import typing -from pathlib import Path - -from google.cloud import storage - - -class Workspace(abc.ABC): - @abc.abstractclassmethod - def __init__(self, base_path: str) -> None: - self.base_path = base_path - - @abc.abstractclassmethod - def read(self, task_id: str, path: str) -> bytes: - pass - - @abc.abstractclassmethod - def write(self, task_id: str, path: str, data: bytes) -> None: - pass - - @abc.abstractclassmethod - def delete( - self, task_id: str, path: str, directory: bool = False, recursive: bool = False - ) -> None: - pass - - @abc.abstractclassmethod - def exists(self, task_id: str, path: str) -> bool: - pass - - @abc.abstractclassmethod - def list(self, task_id: str, path: str) -> typing.List[str]: - pass - - -class LocalWorkspace(Workspace): - def __init__(self, base_path: str): - self.base_path = Path(base_path).resolve() - - def _resolve_path(self, task_id: str, path: str) -> Path: - path = str(path) - path = path if not path.startswith("/") else path[1:] - abs_path = (self.base_path / task_id / path).resolve() - if not str(abs_path).startswith(str(self.base_path)): - print("Error") - raise ValueError(f"Directory traversal is not allowed! - {abs_path}") - try: - abs_path.parent.mkdir(parents=True, exist_ok=True) - except FileExistsError: - pass - return abs_path - - def read(self, task_id: str, path: str) -> bytes: - with open(self._resolve_path(task_id, path), "rb") as f: - return f.read() - - def write(self, task_id: str, path: str, data: bytes) -> None: - file_path = self._resolve_path(task_id, path) - with open(file_path, "wb") as f: - f.write(data) - - def delete( - self, task_id: str, path: str, directory: bool = False, recursive: bool = False - ) -> None: - path = self.base_path / task_id / path - resolved_path = self._resolve_path(task_id, path) - if directory: - if recursive: - os.rmdir(resolved_path) - else: - os.removedirs(resolved_path) - else: - os.remove(resolved_path) - - def exists(self, task_id: str, path: str) -> bool: - path = self.base_path / task_id / path - return self._resolve_path(task_id, path).exists() - - def list(self, task_id: str, path: str) -> typing.List[str]: - path = self.base_path / task_id / path - base = self._resolve_path(task_id, path) - if not base.exists() or not base.is_dir(): - return [] - return [str(p.relative_to(self.base_path / task_id)) for p in base.iterdir()] - - -class GCSWorkspace(Workspace): - def __init__(self, bucket_name: str, base_path: str = ""): - self.bucket_name = bucket_name - self.base_path = Path(base_path).resolve() if base_path else "" - self.storage_client = storage.Client() - self.bucket = self.storage_client.get_bucket(self.bucket_name) - - def _resolve_path(self, task_id: str, path: str) -> Path: - path = str(path) - path = path if not path.startswith("/") else path[1:] - abs_path = (self.base_path / task_id / path).resolve() - if not str(abs_path).startswith(str(self.base_path)): - print("Error") - raise ValueError(f"Directory traversal is not allowed! - {abs_path}") - return abs_path - - def read(self, task_id: str, path: str) -> bytes: - blob = self.bucket.blob(self._resolve_path(task_id, path)) - if not blob.exists(): - raise FileNotFoundError() - return blob.download_as_bytes() - - def write(self, task_id: str, path: str, data: bytes) -> None: - blob = self.bucket.blob(self._resolve_path(task_id, path)) - blob.upload_from_string(data) - - def delete(self, task_id: str, path: str, directory=False, recursive=False): - if directory and not recursive: - raise ValueError("recursive must be True when deleting a directory") - blob = self.bucket.blob(self._resolve_path(task_id, path)) - if not blob.exists(): - return - if directory: - for b in list(self.bucket.list_blobs(prefix=blob.name)): - b.delete() - else: - blob.delete() - - def exists(self, task_id: str, path: str) -> bool: - blob = self.bucket.blob(self._resolve_path(task_id, path)) - return blob.exists() - - def list(self, task_id: str, path: str) -> typing.List[str]: - prefix = os.path.join(task_id, self.base_path, path).replace("\\", "/") + "/" - blobs = list(self.bucket.list_blobs(prefix=prefix)) - return [str(Path(b.name).relative_to(prefix[:-1])) for b in blobs] diff --git a/autogpts/forge/forge/sdk/workspace_test.py b/autogpts/forge/forge/sdk/workspace_test.py deleted file mode 100644 index f259c86b0f..0000000000 --- a/autogpts/forge/forge/sdk/workspace_test.py +++ /dev/null @@ -1,47 +0,0 @@ -import os - -import pytest - -# Assuming the classes are defined in a file named workspace.py -from .workspace import LocalWorkspace - -# Constants -TEST_BASE_PATH = "/tmp/test_workspace" -TEST_FILE_CONTENT = b"Hello World" -TEST_TASK_ID = "1234" - - -# Setup and Teardown for LocalWorkspace - - -@pytest.fixture -def setup_local_workspace(): - os.makedirs(TEST_BASE_PATH, exist_ok=True) - yield - os.system(f"rm -rf {TEST_BASE_PATH}") # Cleanup after tests - - -def test_local_read_write_delete_exists(setup_local_workspace): - workspace = LocalWorkspace(TEST_BASE_PATH) - - # Write - workspace.write(TEST_TASK_ID, "test_file.txt", TEST_FILE_CONTENT) - - # Exists - assert workspace.exists(TEST_TASK_ID, "test_file.txt") - - # Read - assert workspace.read(TEST_TASK_ID, "test_file.txt") == TEST_FILE_CONTENT - - # Delete - workspace.delete(TEST_TASK_ID, "test_file.txt") - assert not workspace.exists(TEST_TASK_ID, "test_file.txt") - - -def test_local_list(setup_local_workspace): - workspace = LocalWorkspace(TEST_BASE_PATH) - workspace.write(TEST_TASK_ID, "test1.txt", TEST_FILE_CONTENT) - workspace.write(TEST_TASK_ID, "test2.txt", TEST_FILE_CONTENT) - - files = workspace.list(TEST_TASK_ID, ".") - assert set(files) == {"test1.txt", "test2.txt"} diff --git a/autogpts/forge/forge/speech/__init__.py b/autogpts/forge/forge/speech/__init__.py deleted file mode 100644 index c5150f6cab..0000000000 --- a/autogpts/forge/forge/speech/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""This module contains the speech recognition and speech synthesis functions.""" -from .say import TextToSpeechProvider, TTSConfig diff --git a/autogpts/forge/forge/utils/yaml_validator.py b/autogpts/forge/forge/utils/yaml_validator.py deleted file mode 100644 index 18a7a63893..0000000000 --- a/autogpts/forge/forge/utils/yaml_validator.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - -import yaml -from colorama import Fore - - -def validate_yaml_file(file: str | Path): - try: - with open(file, encoding="utf-8") as fp: - yaml.load(fp.read(), Loader=yaml.SafeLoader) - except FileNotFoundError: - return (False, f"The file {Fore.CYAN}`{file}`{Fore.RESET} wasn't found") - except yaml.YAMLError as e: - return ( - False, - f"There was an issue while trying to read with your AI Settings file: {e}", - ) - - return (True, f"Successfully validated {Fore.CYAN}`{file}`{Fore.RESET}!") diff --git a/autogpts/forge/mypy.ini b/autogpts/forge/mypy.ini deleted file mode 100644 index 1b1cd5403f..0000000000 --- a/autogpts/forge/mypy.ini +++ /dev/null @@ -1,13 +0,0 @@ -[mypy] -namespace_packages = True -follow_imports = skip -check_untyped_defs = True -disallow_untyped_defs = True -exclude = ^(agbenchmark/challenges/|agent/|venv|venv-dev) -ignore_missing_imports = True - -[mypy-agbenchmark.utils.data_types.*] -ignore_errors = True - -[mypy-numpy.*] -ignore_errors = True diff --git a/autogpts/forge/prompt_settings.yaml b/autogpts/forge/prompt_settings.yaml deleted file mode 100644 index 40ae1f8be2..0000000000 --- a/autogpts/forge/prompt_settings.yaml +++ /dev/null @@ -1,15 +0,0 @@ -constraints: [ - 'Exclusively use the commands listed below.', - 'You can only act proactively, and are unable to start background jobs or set up webhooks for yourself. Take this into account when planning your actions.', - 'You are unable to interact with physical objects. If this is absolutely necessary to fulfill a task or objective or to complete a step, you must ask the user to do it for you. If the user refuses this, and there is no other way to achieve your goals, you must terminate to avoid wasting time and energy.' -] -resources: [ - 'You are a Large Language Model, trained on millions of pages of text, including a lot of factual knowledge. Make use of this factual knowledge to avoid unnecessary gathering of information.' -] -best_practices: [ - 'Continuously review and analyze your actions to ensure you are performing to the best of your abilities.', - 'Constructively self-criticize your big-picture behavior constantly.', - 'Reflect on past decisions and strategies to refine your approach.', - 'Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.', - 'Only make use of your information gathering abilities to find information that you don''t yet have knowledge of.' -] diff --git a/benchmark/.flake8 b/benchmark/.flake8 index bcd424cceb..bdabd9a8d7 100644 --- a/benchmark/.flake8 +++ b/benchmark/.flake8 @@ -1,15 +1,12 @@ [flake8] max-line-length = 88 -select = "E303, W293, W291, W292, E305, E231, E302" +# Ignore rules that conflict with Black code style +extend-ignore = E203, W503 exclude = - .tox, - __pycache__, + __pycache__/, *.pyc, - .env - venv*/*, - .venv/*, - reports/*, - dist/*, - agent/*, - code, - agbenchmark/challenges/* + .pytest_cache/, + venv*/, + .venv/, + reports/, + agbenchmark/reports/, diff --git a/benchmark/.pre-commit-config.yaml b/benchmark/.pre-commit-config.yaml deleted file mode 100644 index 84c7cc0187..0000000000 --- a/benchmark/.pre-commit-config.yaml +++ /dev/null @@ -1,36 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-added-large-files - args: ['--maxkb=500'] - - id: check-byte-order-marker - - id: check-case-conflict - - id: check-merge-conflict - - id: check-symlinks - - id: debug-statements - - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - language_version: python3.10 - - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - language_version: python3.10 - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.3.0' - hooks: - - id: mypy - - - repo: local - hooks: - - id: autoflake - name: autoflake - entry: autoflake --remove-all-unused-imports --recursive --ignore-init-module-imports --ignore-pass-after-docstring --in-place agbenchmark - language: python - types: [ python ] diff --git a/benchmark/agbenchmark/agent_api_interface.py b/benchmark/agbenchmark/agent_api_interface.py index 0bd00a1487..e032cb06d7 100644 --- a/benchmark/agbenchmark/agent_api_interface.py +++ b/benchmark/agbenchmark/agent_api_interface.py @@ -28,7 +28,7 @@ async def run_api_agent( configuration = Configuration(host=config.host) async with ApiClient(configuration) as api_client: api_instance = AgentApi(api_client) - task_request_body = TaskRequestBody(input=task) + task_request_body = TaskRequestBody(input=task, additional_input=None) start_time = time.time() response = await api_instance.create_agent_task( diff --git a/benchmark/agbenchmark/app.py b/benchmark/agbenchmark/app.py index 40fee14b63..40e49db1c8 100644 --- a/benchmark/agbenchmark/app.py +++ b/benchmark/agbenchmark/app.py @@ -106,8 +106,8 @@ def find_agbenchmark_without_uvicorn(): class CreateReportRequest(BaseModel): - test: str = None - test_run_id: str = None + test: str + test_run_id: str # category: Optional[str] = [] mock: Optional[bool] = False @@ -178,8 +178,8 @@ def setup_fastapi_app(agbenchmark_config: AgentBenchmarkConfig) -> FastAPI: logger.debug(f"Benchmark finished running in {time.time() - start_time} s") # List all folders in the current working directory - path_reports = agbenchmark_config.reports_folder - folders = [folder for folder in path_reports.iterdir() if folder.is_dir()] + reports_folder = agbenchmark_config.reports_folder + folders = [folder for folder in reports_folder.iterdir() if folder.is_dir()] # Sort the folders based on their names sorted_folders = sorted(folders, key=lambda x: x.name) @@ -196,13 +196,14 @@ def setup_fastapi_app(agbenchmark_config: AgentBenchmarkConfig) -> FastAPI: data = json.load(file) logger.debug(f"Report data: {data}") else: - logger.error( + raise HTTPException( + 502, "Could not get result after running benchmark: " - f"'report.json' does not exist in '{latest_folder}'" + f"'report.json' does not exist in '{latest_folder}'", ) else: - logger.error( - "Could not get result after running benchmark: no reports found" + raise HTTPException( + 504, "Could not get result after running benchmark: no reports found" ) return data @@ -239,7 +240,9 @@ def setup_fastapi_app(agbenchmark_config: AgentBenchmarkConfig) -> FastAPI: api_instance = AgentApi(api_client) task_input = challenge_info.task - task_request_body = TaskRequestBody(input=task_input) + task_request_body = TaskRequestBody( + input=task_input, additional_input=None + ) task_response = await api_instance.create_agent_task( task_request_body=task_request_body ) @@ -276,7 +279,7 @@ def setup_fastapi_app(agbenchmark_config: AgentBenchmarkConfig) -> FastAPI: # Forward the request response = await client.post( new_url, - data=await request.body(), + content=await request.body(), headers=dict(request.headers), ) diff --git a/benchmark/agbenchmark/challenges/CHALLENGE.md b/benchmark/agbenchmark/challenges/CHALLENGE.md index 203289cbe4..63527414d2 100644 --- a/benchmark/agbenchmark/challenges/CHALLENGE.md +++ b/benchmark/agbenchmark/challenges/CHALLENGE.md @@ -26,7 +26,7 @@ Example: ```json { "category": ["basic"], - "task": "Print the the capital of America to a .txt file", + "task": "Print the capital of America to a .txt file", "dependencies": ["TestWriteFile"], // the class name of the test "ground": { "answer": "Washington", @@ -57,7 +57,7 @@ This is the default method of evaluation. It will compare the files specified in ### python -This runs a python function in the specified "files" which captures the the print statements to be scored using the "should_contain" and "should_not_contain" ground truths. +This runs a python function in the specified "files" which captures the print statements to be scored using the "should_contain" and "should_not_contain" ground truths. ### llm diff --git a/benchmark/agbenchmark/challenges/base.py b/benchmark/agbenchmark/challenges/base.py index f77a08c65e..f2f4fecf1e 100644 --- a/benchmark/agbenchmark/challenges/base.py +++ b/benchmark/agbenchmark/challenges/base.py @@ -1,7 +1,7 @@ import logging from abc import ABC, abstractmethod from pathlib import Path -from typing import AsyncIterator, ClassVar, Optional +from typing import AsyncIterator, Awaitable, ClassVar, Optional import pytest from agent_protocol_client import AgentApi, Step @@ -54,7 +54,7 @@ class BaseChallenge(ABC): config: AgentBenchmarkConfig, request: pytest.FixtureRequest, i_attempt: int, - ) -> None: + ) -> None | Awaitable[None]: """ Test method for use by Pytest-based benchmark sessions. Should return normally if the challenge passes, and raise a (preferably descriptive) error otherwise. diff --git a/benchmark/agbenchmark/challenges/builtin.py b/benchmark/agbenchmark/challenges/builtin.py index 71e61bad4c..b93dc1140e 100644 --- a/benchmark/agbenchmark/challenges/builtin.py +++ b/benchmark/agbenchmark/challenges/builtin.py @@ -1,4 +1,3 @@ -from collections import deque import glob import json import logging @@ -6,19 +5,17 @@ import os import subprocess import sys import tempfile +from collections import deque from pathlib import Path -from typing import Any, ClassVar, Iterator, Literal, Optional +from typing import Annotated, Any, ClassVar, Iterator, Literal, Optional import pytest -from agent_protocol_client import ( - AgentApi, - ApiClient, - Configuration as ClientConfig, - Step, -) +from agent_protocol_client import AgentApi, ApiClient +from agent_protocol_client import Configuration as ClientConfig +from agent_protocol_client import Step from colorama import Fore, Style from openai import _load_client as get_openai_client -from pydantic import BaseModel, constr, Field, validator +from pydantic import BaseModel, Field, constr, validator from agbenchmark.agent_api_interface import download_agent_artifacts_into_folder from agbenchmark.agent_interface import copy_challenge_artifacts_into_workspace @@ -49,7 +46,7 @@ class BuiltinChallengeSpec(BaseModel): class Info(BaseModel): difficulty: DifficultyLevel - description: constr(regex=r"^Tests if the agent can.*") + description: Annotated[str, constr(regex=r"^Tests if the agent can.*")] side_effects: list[str] = Field(default_factory=list) info: Info @@ -184,7 +181,7 @@ class BuiltinChallenge(BaseChallenge): steps: list[Step] = [] try: async for step in self.run_challenge( - config, timeout, mock=request.config.getoption("--mock") + config, timeout, mock=bool(request.config.getoption("--mock")) ): if not task_id: task_id = step.task_id @@ -199,6 +196,8 @@ class BuiltinChallenge(BaseChallenge): timed_out = False except TimeoutError: timed_out = True + + assert isinstance(request.node, pytest.Item) request.node.user_properties.append(("steps", steps)) request.node.user_properties.append(("n_steps", n_steps)) request.node.user_properties.append(("timed_out", timed_out)) @@ -340,8 +339,11 @@ class BuiltinChallenge(BaseChallenge): capture_output=True, text=True, ) + logger.debug(f"EXIT CODE: {result.returncode}") + logger.debug(f"STDOUT: {result.stdout}") + logger.debug(f"STDERR: {result.stderr}") if "error" in result.stderr or result.returncode != 0: - yield "pytest", f"Error: {result.stderr}\n" + yield "pytest", f"Error: {result.stderr.strip() or result.stdout}\n" else: yield "pytest", f"Output: {result.stdout}\n" @@ -411,15 +413,10 @@ class BuiltinChallenge(BaseChallenge): def load_builtin_challenges() -> Iterator[type[BuiltinChallenge]]: logger.info("Loading built-in challenges...") - challenges_path = os.path.dirname(__file__) + challenges_path = Path(__file__).parent logger.debug(f"Looking for challenge spec files in {challenges_path}...") - json_files = deque( - glob.glob( - f"{challenges_path}/**/data.json", - recursive=True, - ) - ) + json_files = deque(challenges_path.rglob("data.json")) logger.debug(f"Found {len(json_files)} built-in challenges.") @@ -431,7 +428,7 @@ def load_builtin_challenges() -> Iterator[type[BuiltinChallenge]]: ignored += 1 continue - challenge = BuiltinChallenge.from_challenge_spec_file(Path(json_file)) + challenge = BuiltinChallenge.from_challenge_spec_file(json_file) logger.debug(f"Generated test for {challenge.info.name}") yield challenge @@ -442,8 +439,8 @@ def load_builtin_challenges() -> Iterator[type[BuiltinChallenge]]: ) -def _challenge_should_be_ignored(json_file_path: str): +def _challenge_should_be_ignored(json_file_path: Path): return ( - "challenges/deprecated" in json_file_path - or "challenges/library" in json_file_path + "challenges/deprecated" in json_file_path.as_posix() + or "challenges/library" in json_file_path.as_posix() ) diff --git a/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_in/test.py b/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_in/test.py index 76a2e299b9..77d06412a7 100644 --- a/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_in/test.py +++ b/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_in/test.py @@ -23,9 +23,10 @@ def test_get_ethereum_price() -> None: real_eth_price_value = float(real_eth_price) # Check if the eth price is within $50 of the actual Ethereum price - assert ( - abs(real_eth_price_value - eth_price_value) <= 50 - ), f"AssertionError: Ethereum price is not within $50 of the actual Ethereum price (Provided price: ${eth_price}, Real price: ${real_eth_price})" + assert abs(real_eth_price_value - eth_price_value) <= 50, ( + "AssertionError: Ethereum price is not within $50 of the actual Ethereum price " + f"(Provided price: ${eth_price}, Real price: ${real_eth_price})" + ) print("Matches") diff --git a/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_out/test.py b/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_out/test.py index e64a7d52ca..db13805ff3 100644 --- a/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_out/test.py +++ b/benchmark/agbenchmark/challenges/library/ethereum/check_price/artifacts_out/test.py @@ -23,9 +23,10 @@ def test_get_ethereum_price() -> None: real_eth_price_value = float(real_eth_price) # Check if the eth price is within $50 of the actual Ethereum price - assert ( - abs(real_eth_price_value - eth_price_value) <= 50 - ), f"AssertionError: Ethereum price is not within $50 of the actual Ethereum price (Provided price: ${eth_price}, Real price: ${real_eth_price})" + assert abs(real_eth_price_value - eth_price_value) <= 50, ( + "AssertionError: Ethereum price is not within $50 of the actual Ethereum price " + f"(Provided price: ${eth_price}, Real price: ${real_eth_price})" + ) print("Matches") diff --git a/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/artifacts_out/sample_code.py b/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/artifacts_out/sample_code.py index 6056691daf..8e2ddae318 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/artifacts_out/sample_code.py +++ b/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/artifacts_out/sample_code.py @@ -1,4 +1,3 @@ -# mypy: ignore-errors from typing import List, Optional diff --git a/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/custom_python/test.py b/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/custom_python/test.py index 49070d1b85..7737140b79 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/custom_python/test.py +++ b/benchmark/agbenchmark/challenges/verticals/code/1_three_sum/custom_python/test.py @@ -1,4 +1,4 @@ -# mypy: ignore-errors +# pyright: reportMissingImports=false from typing import List from sample_code import three_sum diff --git a/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/artifacts_out/password_generator.py b/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/artifacts_out/password_generator.py index 5797ebcb21..03eefbc829 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/artifacts_out/password_generator.py +++ b/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/artifacts_out/password_generator.py @@ -21,7 +21,6 @@ def generate_password(length: int = 8) -> str: if __name__ == "__main__": password_length = ( - int(sys.argv[sys.argv.index("--length") + 1]) - if "--length" in sys.argv else 8 + int(sys.argv[sys.argv.index("--length") + 1]) if "--length" in sys.argv else 8 ) print(generate_password(password_length)) diff --git a/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/custom_python/test.py b/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/custom_python/test.py index 86ce911ab8..7af27a5924 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/custom_python/test.py +++ b/benchmark/agbenchmark/challenges/verticals/code/2_password_generator/custom_python/test.py @@ -1,3 +1,4 @@ +# pyright: reportMissingImports=false import unittest import password_generator @@ -18,7 +19,9 @@ class TestPasswordGenerator(unittest.TestCase): def test_password_content(self): password = password_generator.generate_password() self.assertTrue(any(c.isdigit() for c in password)) - self.assertTrue(any(c in password_generator.string.punctuation for c in password)) + self.assertTrue( + any(c in password_generator.string.punctuation for c in password) + ) if __name__ == "__main__": diff --git a/benchmark/agbenchmark/challenges/verticals/code/4_url_shortener/custom_python/test.py b/benchmark/agbenchmark/challenges/verticals/code/4_url_shortener/custom_python/test.py index c3daffa80b..8266747e4a 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/4_url_shortener/custom_python/test.py +++ b/benchmark/agbenchmark/challenges/verticals/code/4_url_shortener/custom_python/test.py @@ -1,3 +1,4 @@ +# pyright: reportMissingImports=false import unittest from url_shortener import retrieve_url, shorten_url diff --git a/benchmark/agbenchmark/challenges/verticals/code/5_tic_tac_toe/artifacts_out/tic_tac_toe.py b/benchmark/agbenchmark/challenges/verticals/code/5_tic_tac_toe/artifacts_out/tic_tac_toe.py index e0163220a1..5f2798d142 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/5_tic_tac_toe/artifacts_out/tic_tac_toe.py +++ b/benchmark/agbenchmark/challenges/verticals/code/5_tic_tac_toe/artifacts_out/tic_tac_toe.py @@ -56,7 +56,7 @@ def winner(board): def getLocation(): location = input( - "Choose where to play. Enter two numbers separated by a comma, for example: 1,1 " + "Choose where to play. Enter two numbers separated by a comma [example: 1,1]: " ) print(f"\nYou picked {location}") coordinates = [int(x) for x in location.split(",")] @@ -69,7 +69,8 @@ def getLocation(): ): print("You inputted a location in an invalid format") location = input( - "Choose where to play. Enter two numbers separated by a comma, for example: 1,1 " + "Choose where to play. Enter two numbers separated by a comma " + "[example: 1,1]: " ) coordinates = [int(x) for x in location.split(",")] return coordinates diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/abstract_class.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/abstract_class.py index dec3bcb47f..1add2a330d 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/abstract_class.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/abstract_class.py @@ -37,15 +37,14 @@ class GameStatus(BaseModel): winner: Optional[str] -from typing import List - - class Game(BaseModel): game_id: str - players: List[str] - board: dict # This could represent the state of the game board, you might need to flesh this out further - ships: List[ShipPlacement] # List of ship placements for this game - turns: List[Turn] # List of turns that have been taken + players: list[str] + # This could represent the state of the game board, + # you might need to flesh this out further: + board: dict + ships: list[ShipPlacement] # List of ship placements for this game + turns: list[Turn] # List of turns that have been taken class AbstractBattleship(ABC): @@ -86,7 +85,7 @@ class AbstractBattleship(ABC): pass @abstractmethod - def get_game(self) -> Game: + def get_game(self) -> Game | None: """ Retrieve the state of the game. """ @@ -103,5 +102,8 @@ class AbstractBattleship(ABC): def create_game(self) -> None: """ Create a new game. + + Returns: + str: The ID of the created game. """ pass diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/conftest.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/conftest.py index a1412966b4..4d0928ec9f 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/conftest.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/conftest.py @@ -1,3 +1,4 @@ +# pyright: reportMissingImports=false import pytest from abstract_class import ShipPlacement, Turn from battleship import Battleship diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_negative.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_negative.py index 34bed48b4b..c9901377e5 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_negative.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_negative.py @@ -50,7 +50,7 @@ def test_cant_hit_before_ships_placed(battleship_game): def test_cant_place_ship_after_all_ships_placed(battleship_game, initialized_game_id): - game = battleship_game.get_game(initialized_game_id) + battleship_game.get_game(initialized_game_id) additional_ship = ShipPlacement( ship_type="carrier", start={"row": 2, "column": "E"}, direction="horizontal" ) diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_positive.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_positive.py index 203b90ca94..9c9fd8a215 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_positive.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_in/test_positive.py @@ -61,6 +61,7 @@ def test_ship_sinking_feedback(battleship_game, initialized_game_id): {"row": 1, "column": "H"}, ] + response = None for index, hit in enumerate(hits): turn = Turn(target={"row": 2, "column": hit}) response = battleship_game.create_turn(initialized_game_id, turn) @@ -69,7 +70,7 @@ def test_ship_sinking_feedback(battleship_game, initialized_game_id): static_turn = Turn(target=static_moves[index]) battleship_game.create_turn(initialized_game_id, static_turn) - assert response.result == "sunk" + assert response and response.result == "sunk" def test_restart_game(battleship_game): diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/abstract_class.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/abstract_class.py index dec3bcb47f..9a1ba446fe 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/abstract_class.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/abstract_class.py @@ -37,15 +37,14 @@ class GameStatus(BaseModel): winner: Optional[str] -from typing import List - - class Game(BaseModel): game_id: str - players: List[str] - board: dict # This could represent the state of the game board, you might need to flesh this out further - ships: List[ShipPlacement] # List of ship placements for this game - turns: List[Turn] # List of turns that have been taken + players: list[str] + # This could represent the state of the game board, + # you might need to flesh this out further: + board: dict + ships: list[ShipPlacement] # List of ship placements for this game + turns: list[Turn] # List of turns that have been taken class AbstractBattleship(ABC): @@ -86,7 +85,7 @@ class AbstractBattleship(ABC): pass @abstractmethod - def get_game(self) -> Game: + def get_game(self, game_id: str) -> Game | None: """ Retrieve the state of the game. """ @@ -100,8 +99,11 @@ class AbstractBattleship(ABC): pass @abstractmethod - def create_game(self) -> None: + def create_game(self) -> str: """ Create a new game. + + Returns: + str: The ID of the created game. """ pass diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/battleship.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/battleship.py index 1fe3047275..e9aa5eafa4 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/battleship.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/battleship.py @@ -1,14 +1,20 @@ from typing import Dict -from abstract_class import (AbstractBattleship, Game, GameStatus, - ShipPlacement, Turn, TurnResponse) +from abstract_class import ( + AbstractBattleship, + Game, + GameStatus, + ShipPlacement, + Turn, + TurnResponse, +) class Battleship(AbstractBattleship): def __init__(self): - self.games: Dict[int, Game] = {} + self.games: Dict[str, Game] = {} - def create_game(self) -> int: + def create_game(self) -> str: game_id = str(len(self.games)) new_game = Game( game_id=game_id, @@ -19,7 +25,7 @@ class Battleship(AbstractBattleship): ) self.games[game_id] = new_game - return new_game.game_id + return game_id def create_ship_placement(self, game_id: str, placement: ShipPlacement) -> None: game = self.games.get(game_id) @@ -79,38 +85,34 @@ class Battleship(AbstractBattleship): game.turns.append(turn) - if hit_ship == "hit": + if not hit_ship or hit_ship == "hit": # if no ship or already hit return TurnResponse(result="miss", ship_type=None) - if hit_ship: - ship_placement = next(sp for sp in game.ships if sp.ship_type == hit_ship) + ship_placement = next(sp for sp in game.ships if sp.ship_type == hit_ship) + start_row, start_col = ( + ship_placement.start["row"], + ord(ship_placement.start["column"]) - ord("A"), + ) + ship_positions = [ + ( + start_row + (i if ship_placement.direction == "vertical" else 0), + start_col + (i if ship_placement.direction == "horizontal" else 0), + ) + for i in range(self.SHIP_LENGTHS[hit_ship]) + ] - if hit_ship: - ship_placement = next(sp for sp in game.ships if sp.ship_type == hit_ship) - start_row, start_col = ship_placement.start["row"], ord( - ship_placement.start["column"] - ) - ord("A") - ship_positions = [ - ( - start_row + (i if ship_placement.direction == "vertical" else 0), - start_col + (i if ship_placement.direction == "horizontal" else 0), - ) - for i in range(self.SHIP_LENGTHS[hit_ship]) - ] + targeted_positions = { + (t.target["row"], ord(t.target["column"]) - ord("A")) for t in game.turns + } - targeted_positions = { - (t.target["row"], ord(t.target["column"]) - ord("A")) - for t in game.turns - } + game.board[(target_row, target_col)] = "hit" - game.board[(target_row, target_col)] = "hit" - - if set(ship_positions).issubset(targeted_positions): - for pos in ship_positions: - game.board[pos] = "hit" - return TurnResponse(result="sunk", ship_type=hit_ship) - else: - return TurnResponse(result="hit", ship_type=hit_ship) + if set(ship_positions).issubset(targeted_positions): + for pos in ship_positions: + game.board[pos] = "hit" + return TurnResponse(result="sunk", ship_type=hit_ship) + else: + return TurnResponse(result="hit", ship_type=hit_ship) def get_game_status(self, game_id: str) -> GameStatus: game = self.games.get(game_id) @@ -132,12 +134,12 @@ class Battleship(AbstractBattleship): def get_winner(self, game_id: str) -> str: game_status = self.get_game_status(game_id) - if game_status.is_game_over: + if game_status.is_game_over and game_status.winner: return game_status.winner else: - return None + raise ValueError(f"Game {game_id} isn't over yet") - def get_game(self, game_id: str) -> Game: + def get_game(self, game_id: str) -> Game | None: return self.games.get(game_id) def delete_game(self, game_id: str) -> None: diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_negative.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_negative.py index 34bed48b4b..c9901377e5 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_negative.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_negative.py @@ -50,7 +50,7 @@ def test_cant_hit_before_ships_placed(battleship_game): def test_cant_place_ship_after_all_ships_placed(battleship_game, initialized_game_id): - game = battleship_game.get_game(initialized_game_id) + battleship_game.get_game(initialized_game_id) additional_ship = ShipPlacement( ship_type="carrier", start={"row": 2, "column": "E"}, direction="horizontal" ) diff --git a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_positive.py b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_positive.py index 203b90ca94..9c9fd8a215 100644 --- a/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_positive.py +++ b/benchmark/agbenchmark/challenges/verticals/code/6_battleship/artifacts_out/test_positive.py @@ -61,6 +61,7 @@ def test_ship_sinking_feedback(battleship_game, initialized_game_id): {"row": 1, "column": "H"}, ] + response = None for index, hit in enumerate(hits): turn = Turn(target={"row": 2, "column": hit}) response = battleship_game.create_turn(initialized_game_id, turn) @@ -69,7 +70,7 @@ def test_ship_sinking_feedback(battleship_game, initialized_game_id): static_turn = Turn(target=static_moves[index]) battleship_game.create_turn(initialized_game_id, static_turn) - assert response.result == "sunk" + assert response and response.result == "sunk" def test_restart_game(battleship_game): diff --git a/benchmark/agbenchmark/challenges/webarena.py b/benchmark/agbenchmark/challenges/webarena.py index 9f44ac8f41..4a25e1a57c 100644 --- a/benchmark/agbenchmark/challenges/webarena.py +++ b/benchmark/agbenchmark/challenges/webarena.py @@ -6,7 +6,7 @@ from typing import ClassVar, Iterator, Literal import pytest import requests from agent_protocol_client import AgentApi, Step -from pydantic import BaseModel, validator, ValidationError +from pydantic import BaseModel, ValidationError, validator from agbenchmark.config import AgentBenchmarkConfig from agbenchmark.utils.data_types import Category, EvalResult @@ -93,11 +93,12 @@ class Eval(ABC): ... -class StringEval(BaseModel, Eval): - type: ReferenceAnswerType +class BaseStringEval(BaseModel, Eval): + # type: ReferenceAnswerType + pass -class ExactStringMatchEval(StringEval): +class ExactStringMatchEval(BaseStringEval): type: Literal["exact_match"] = "exact_match" reference_answer: str @@ -109,7 +110,7 @@ class ExactStringMatchEval(StringEval): return string == self.reference_answer -class FuzzyStringMatchEval(StringEval): +class FuzzyStringMatchEval(BaseStringEval): type: Literal["fuzzy_match"] = "fuzzy_match" reference_answer: str @@ -122,7 +123,7 @@ class FuzzyStringMatchEval(StringEval): return self.reference_answer.lower() in string.lower() -class MustIncludeStringEval(StringEval): +class MustIncludeStringEval(BaseStringEval): type: Literal["must_include"] = "must_include" reference_answer: str @@ -134,6 +135,9 @@ class MustIncludeStringEval(StringEval): return self.reference_answer.lower() in string.lower() +StringEval = ExactStringMatchEval | FuzzyStringMatchEval | MustIncludeStringEval + + class UrlMatchEval(BaseModel, Eval): url: str """Example: `"__WIKI__/wiki/Octopus"`""" @@ -142,8 +146,8 @@ class UrlMatchEval(BaseModel, Eval): def description(self) -> str: return f"Agent must navigate to '{self.url}'" - def evaluate(self, url: str) -> bool: - return url == resolve_uri(self.url) + def evaluate(self, string: str) -> bool: + return string == resolve_uri(self.url) class ProgramHtmlEval(BaseModel): @@ -258,7 +262,8 @@ class WebArenaChallengeSpec(BaseModel): f"{' and '.join(s.base_url for s in sites)}.\n\n" + "\n".join( s.additional_info.format(url=s.base_url) - for s in sites if s.additional_info + for s in sites + if s.additional_info ) ).strip() @@ -391,7 +396,9 @@ class WebArenaChallenge(BaseChallenge): if request.config.getoption("--nc"): timeout = 100000 elif cutoff := request.config.getoption("--cutoff"): - timeout = int(cutoff) + timeout = int(cutoff) # type: ignore + + assert isinstance(request.node, pytest.Item) n_steps = 0 timed_out = None @@ -400,7 +407,7 @@ class WebArenaChallenge(BaseChallenge): eval_results_per_step: list[list[tuple[_Eval, EvalResult]]] = [] try: async for step in self.run_challenge( - config, timeout, mock=request.config.getoption("--mock") + config, timeout, mock=bool(request.config.getoption("--mock")) ): if not step.output: logger.warn(f"Step has no output: {step}") @@ -415,7 +422,7 @@ class WebArenaChallenge(BaseChallenge): ) step_eval_results = self.evaluate_step_result( - step, mock=request.config.getoption("--mock") + step, mock=bool(request.config.getoption("--mock")) ) logger.debug(f"Intermediary results: {step_eval_results}") eval_results_per_step.append(step_eval_results) @@ -462,7 +469,7 @@ class WebArenaChallenge(BaseChallenge): def load_webarena_challenges( - skip_unavailable: bool = True + skip_unavailable: bool = True, ) -> Iterator[type[WebArenaChallenge]]: logger.info("Loading WebArena challenges...") diff --git a/benchmark/agbenchmark/conftest.py b/benchmark/agbenchmark/conftest.py index cf40493b54..886605205c 100644 --- a/benchmark/agbenchmark/conftest.py +++ b/benchmark/agbenchmark/conftest.py @@ -123,8 +123,10 @@ def check_regression(request: pytest.FixtureRequest) -> None: with contextlib.suppress(FileNotFoundError): rt_tracker = RegressionTestsTracker(agbenchmark_config.regression_tests_file) + assert isinstance(request.node, pytest.Function) + assert isinstance(request.node.parent, pytest.Class) test_name = request.node.parent.name - challenge_location = getattr(request.node.parent.cls, "CHALLENGE_LOCATION", "") + challenge_location = getattr(request.node.cls, "CHALLENGE_LOCATION", "") skip_string = f"Skipping {test_name} at {challenge_location}" # Check if the test name exists in the regression tests @@ -148,7 +150,9 @@ def mock(request: pytest.FixtureRequest) -> bool: Returns: bool: Whether `--mock` is set for this session. """ - return request.config.getoption("--mock") + mock = request.config.getoption("--mock") + assert isinstance(mock, bool) + return mock test_reports: dict[str, Test] = {} @@ -221,7 +225,7 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): def pytest_collection_modifyitems( - items: list[pytest.Item], config: pytest.Config + items: list[pytest.Function], config: pytest.Config ) -> None: """ Pytest hook that is called after initial test collection has been performed. @@ -248,8 +252,9 @@ def pytest_collection_modifyitems( i = 0 while i < len(items): item = items[i] + assert item.cls and issubclass(item.cls, BaseChallenge) challenge = item.cls - challenge_name = item.cls.__name__ + challenge_name = challenge.info.name if not issubclass(challenge, BaseChallenge): item.warn( diff --git a/benchmark/agbenchmark/main.py b/benchmark/agbenchmark/main.py index 4cd97bd89f..27985a5078 100644 --- a/benchmark/agbenchmark/main.py +++ b/benchmark/agbenchmark/main.py @@ -18,9 +18,9 @@ def run_benchmark( maintain: bool = False, improve: bool = False, explore: bool = False, - tests: tuple[str] = tuple(), - categories: tuple[str] = tuple(), - skip_categories: tuple[str] = tuple(), + tests: tuple[str, ...] = tuple(), + categories: tuple[str, ...] = tuple(), + skip_categories: tuple[str, ...] = tuple(), attempts_per_challenge: int = 1, mock: bool = False, no_dep: bool = False, diff --git a/benchmark/agbenchmark/reports/ReportManager.py b/benchmark/agbenchmark/reports/ReportManager.py index 24a9893d4c..76e0cab8b1 100644 --- a/benchmark/agbenchmark/reports/ReportManager.py +++ b/benchmark/agbenchmark/reports/ReportManager.py @@ -53,9 +53,9 @@ class SingletonReportManager: @classmethod def clear_instance(cls): cls.instance = None - cls.INFO_MANAGER = None - cls.REGRESSION_MANAGER = None - cls.SUCCESS_RATE_TRACKER = None + del cls.INFO_MANAGER + del cls.REGRESSION_MANAGER + del cls.SUCCESS_RATE_TRACKER class BaseReportManager: @@ -99,7 +99,8 @@ class BaseReportManager: class SessionReportManager(BaseReportManager): """Abstracts interaction with the regression tests file""" - tests: dict[str, Test] | Report + tests: dict[str, Test] + report: Report | None = None def __init__(self, report_file: Path, benchmark_start_time: datetime): super().__init__(report_file) @@ -109,20 +110,21 @@ class SessionReportManager(BaseReportManager): def save(self) -> None: with self.report_file.open("w") as f: - if isinstance(self.tests, Report): - f.write(self.tests.json(indent=4)) + if self.report: + f.write(self.report.json(indent=4)) else: json.dump({k: v.dict() for k, v in self.tests.items()}, f, indent=4) def load(self) -> None: super().load() - if "tests" in self.tests: # type: ignore - self.tests = Report.parse_obj(self.tests) + + if "tests" in self.tests: + self.report = Report.parse_obj(self.tests) else: self.tests = {n: Test.parse_obj(d) for n, d in self.tests.items()} def add_test_report(self, test_name: str, test_report: Test) -> None: - if isinstance(self.tests, Report): + if self.report: raise RuntimeError("Session report already finalized") if test_name.startswith("Test"): @@ -134,10 +136,10 @@ class SessionReportManager(BaseReportManager): def finalize_session_report(self, config: AgentBenchmarkConfig) -> None: command = " ".join(sys.argv) - if isinstance(self.tests, Report): + if self.report: raise RuntimeError("Session report already finalized") - self.tests = Report( + self.report = Report( command=command.split(os.sep)[-1], benchmark_git_commit_sha="---", agent_git_commit_sha="---", @@ -156,7 +158,7 @@ class SessionReportManager(BaseReportManager): config=config.dict(exclude={"reports_folder"}, exclude_none=True), ) - agent_categories = get_highest_achieved_difficulty_per_category(self.tests) + agent_categories = get_highest_achieved_difficulty_per_category(self.report) if len(agent_categories) > 1: save_single_radar_chart( agent_categories, @@ -166,8 +168,8 @@ class SessionReportManager(BaseReportManager): self.save() def get_total_costs(self): - if isinstance(self.tests, Report): - tests = self.tests.tests + if self.report: + tests = self.report.tests else: tests = self.tests diff --git a/benchmark/agbenchmark/reports/processing/report_types.py b/benchmark/agbenchmark/reports/processing/report_types.py index ea2ad840f9..0900e21bd0 100644 --- a/benchmark/agbenchmark/reports/processing/report_types.py +++ b/benchmark/agbenchmark/reports/processing/report_types.py @@ -3,7 +3,7 @@ Model definitions used internally and for reports generated during command-line """ import logging -from typing import Any, Dict, List +from typing import Annotated, Any, Dict, List from agent_protocol_client import Step from pydantic import BaseModel, Field, constr, validator @@ -88,7 +88,7 @@ class Test(BaseModel): class ReportBase(BaseModel): command: str completion_time: str | None = None - benchmark_start_time: constr(regex=datetime_format) + benchmark_start_time: Annotated[str, constr(regex=datetime_format)] metrics: MetricsOverall config: Dict[str, str | dict[str, str]] agent_git_commit_sha: str | None = None diff --git a/benchmark/agbenchmark/reports/processing/report_types_v2.py b/benchmark/agbenchmark/reports/processing/report_types_v2.py index b26adaa6d4..6ae0aa5c90 100644 --- a/benchmark/agbenchmark/reports/processing/report_types_v2.py +++ b/benchmark/agbenchmark/reports/processing/report_types_v2.py @@ -1,4 +1,5 @@ """Model definitions for use in the API""" +from typing import Annotated from pydantic import BaseModel, constr @@ -36,7 +37,7 @@ class RunDetails(BaseModel): run_id: str | None = None command: str completion_time: str | None = None - benchmark_start_time: constr(regex=datetime_format) + benchmark_start_time: Annotated[str, constr(regex=datetime_format)] class BenchmarkRun(BaseModel): diff --git a/benchmark/agbenchmark/schema.py b/benchmark/agbenchmark/schema.py index 2aed562da5..190710c357 100644 --- a/benchmark/agbenchmark/schema.py +++ b/benchmark/agbenchmark/schema.py @@ -1,14 +1,10 @@ from __future__ import annotations -from typing import Optional +from typing import Any, Optional from pydantic import BaseModel, Field -class TaskInput(BaseModel): - pass - - class TaskRequestBody(BaseModel): input: str = Field( ..., @@ -16,7 +12,7 @@ class TaskRequestBody(BaseModel): description="Input prompt for the task.", example="Write the words you receive to the file 'output.txt'.", ) - additional_input: Optional[TaskInput] = {} + additional_input: Optional[dict[str, Any]] = Field(default_factory=dict) class TaskEvalRequestBody(TaskRequestBody): diff --git a/benchmark/agbenchmark/utils/dependencies/__init__.py b/benchmark/agbenchmark/utils/dependencies/__init__.py index 2d84019464..0c6079177a 100644 --- a/benchmark/agbenchmark/utils/dependencies/__init__.py +++ b/benchmark/agbenchmark/utils/dependencies/__init__.py @@ -32,7 +32,10 @@ def _add_ini_and_option( default: str | bool | int, **kwargs: Any, ) -> None: - """Add an option to both the ini file as well as the command line flags, with the latter overriding the former.""" + """ + Add an option to both the ini file and the command line flags. + Command line flags/options takes precedence over the ini config. + """ parser.addini( name, help + " This overrides the similarly named option from the config.", @@ -44,7 +47,10 @@ def _add_ini_and_option( def _get_ini_or_option( config: Any, name: str, choices: Optional[list[str]] ) -> str | None: - """Get an option from either the ini file or the command line flags, the latter taking precedence.""" + """ + Get an option from either the ini file or the command line flags, + with the latter taking precedence. + """ value = config.getini(name) if value is not None and choices is not None and value not in choices: raise ValueError( @@ -73,7 +79,7 @@ def pytest_addoption(parser: Parser) -> None: default=False, help=( "List all non-nodeid dependency names + the tests they resolve to. " - "Will also list all nodeid dependency names when verbosity is high enough." + "Will also list all nodeid dependency names in verbose mode." ), ) @@ -83,7 +89,10 @@ def pytest_addoption(parser: Parser) -> None: "--list-processed-dependencies", action="store_true", default=False, - help="List all dependencies of all tests as a list of nodeids + the names that could not be resolved.", + help=( + "List all dependencies of all tests as a list of nodeids " + "+ the names that could not be resolved." + ), ) # Add an ini option + flag to choose the action to take for failed dependencies @@ -94,7 +103,8 @@ def pytest_addoption(parser: Parser) -> None: name="failed_dependency_action", help=( "The action to take when a test has dependencies that failed. " - 'Use "run" to run the test anyway, "skip" to skip the test, and "fail" to fail the test.' + 'Use "run" to run the test anyway, "skip" to skip the test, ' + 'and "fail" to fail the test.' ), default="skip", choices=DEPENDENCY_PROBLEM_ACTIONS.keys(), @@ -107,8 +117,10 @@ def pytest_addoption(parser: Parser) -> None: group, name="missing_dependency_action", help=( - "The action to take when a test has dependencies that cannot be found within the current scope. " - 'Use "run" to run the test anyway, "skip" to skip the test, and "fail" to fail the test.' + "The action to take when a test has dependencies that cannot be found " + "within the current scope. " + 'Use "run" to run the test anyway, "skip" to skip the test, ' + 'and "fail" to fail the test.' ), default="warning", choices=DEPENDENCY_PROBLEM_ACTIONS.keys(), @@ -134,12 +146,12 @@ def pytest_configure(config: Any) -> None: # Register marker config.addinivalue_line( "markers", - "depends(name='name', on=['other_name']): marks depencies between tests.", + "depends(name='name', on=['other_name']): marks dependencies between tests.", ) @pytest.hookimpl(trylast=True) -def pytest_collection_modifyitems(config: Any, items: list[Item]) -> None: +def pytest_collection_modifyitems(config: Any, items: list[pytest.Function]) -> None: manager = managers[-1] # Register the founds tests on the manager diff --git a/benchmark/agbenchmark/utils/dependencies/constants.py b/benchmark/agbenchmark/utils/dependencies/constants.py index 98f60a5e9a..14aa60da60 100644 --- a/benchmark/agbenchmark/utils/dependencies/constants.py +++ b/benchmark/agbenchmark/utils/dependencies/constants.py @@ -3,7 +3,7 @@ # The name of the marker used MARKER_NAME = "depends" -# The name of the keyword argument for the marker that contains custom name(s) for the tests +# The name of the kwarg for 'depends' markers that contains custom name(s) for the tests MARKER_KWARG_ID = "name" # The name of the keyword argument for the marker that specifies the tests to depend on diff --git a/benchmark/agbenchmark/utils/dependencies/graphs.py b/benchmark/agbenchmark/utils/dependencies/graphs.py index 47d3d5c095..91fed2110c 100644 --- a/benchmark/agbenchmark/utils/dependencies/graphs.py +++ b/benchmark/agbenchmark/utils/dependencies/graphs.py @@ -57,8 +57,10 @@ def curved_edges( """ ax = plt.gca() for u, v, data in G.edges(data=True): - src = np.array(pos[u]) - dst = np.array(pos[v]) + _src = pos[u] + _dst = pos[v] + src = np.array(_src) + dst = np.array(_dst) same_level = abs(src[1] - dst[1]) < 0.01 @@ -68,7 +70,7 @@ def curved_edges( arrow = patches.FancyArrowPatch( posA=curve[0], # type: ignore posB=curve[-1], # type: ignore - connectionstyle=f"arc3,rad=0.2", + connectionstyle="arc3,rad=0.2", color="gray", arrowstyle="-|>", mutation_scale=15.0, @@ -80,8 +82,8 @@ def curved_edges( else: ax.annotate( "", - xy=dst, - xytext=src, + xy=_dst, + xytext=_src, arrowprops=dict( arrowstyle="-|>", color="gray", lw=1, shrinkA=10, shrinkB=10 ), @@ -89,7 +91,8 @@ def curved_edges( def tree_layout(graph: nx.DiGraph, root_node: Any) -> Dict[Any, Tuple[float, float]]: - """Compute positions as a tree layout centered on the root with alternating vertical shifts.""" + """Compute positions as a tree layout centered on the root + with alternating vertical shifts.""" bfs_tree = nx.bfs_tree(graph, source=root_node) levels = { node: depth @@ -137,7 +140,7 @@ def tree_layout(graph: nx.DiGraph, root_node: Any) -> Dict[Any, Tuple[float, flo def graph_spring_layout( dag: nx.DiGraph, labels: Dict[Any, str], tree: bool = True ) -> None: - num_nodes = len(dag.nodes()) + num_nodes = len(list(dag.nodes())) # Setting up the figure and axis fig, ax = plt.subplots() ax.axis("off") # Turn off the axis @@ -288,7 +291,8 @@ def graph_interactive_network( # Optionally, save to a file # Sync with the flutter UI - # this literally only works in the AutoGPT repo, but this part of the code is not reached if BUILD_SKILL_TREE is false + # this literally only works in the AutoGPT repo, but this part of the code + # is not reached if BUILD_SKILL_TREE is false write_pretty_json(graph_data, flutter_app_path / "tree_structure.json") validate_skill_tree(graph_data, "") @@ -332,11 +336,13 @@ def graph_interactive_network( def extract_subgraph_based_on_category(graph, category): """ - Extracts a subgraph that includes all nodes and edges required to reach all nodes with a specified category. + Extracts a subgraph that includes all nodes and edges required to reach all nodes + with a specified category. :param graph: The original graph. :param category: The target category. - :return: Subgraph with nodes and edges required to reach the nodes with the given category. + :return: Subgraph with nodes and edges required to reach the nodes + with the given category. """ subgraph = {"nodes": [], "edges": []} @@ -424,7 +430,8 @@ def get_roots(graph): def validate_skill_tree(graph, skill_tree_name): """ - Validate if a given graph represents a valid skill tree and raise appropriate exceptions if not. + Validate if a given graph represents a valid skill tree + and raise appropriate exceptions if not. :param graph: A dictionary representing the graph with 'nodes' and 'edges'. :raises: ValueError with a description of the invalidity. @@ -434,7 +441,8 @@ def validate_skill_tree(graph, skill_tree_name): if cycle_path: cycle_str = " -> ".join(cycle_path) raise ValueError( - f"{skill_tree_name} skill tree is circular! Circular path detected: {cycle_str}." + f"{skill_tree_name} skill tree is circular! " + f"Detected circular path: {cycle_str}." ) # Check for multiple roots diff --git a/benchmark/agbenchmark/utils/dependencies/main.py b/benchmark/agbenchmark/utils/dependencies/main.py index 7dab3b51bd..3127a46a0e 100644 --- a/benchmark/agbenchmark/utils/dependencies/main.py +++ b/benchmark/agbenchmark/utils/dependencies/main.py @@ -1,18 +1,19 @@ """ A module to manage dependencies between pytest tests. -This module provides the methods implementing the main logic. These are used in the pytest hooks that are in -__init__.py. +This module provides the methods implementing the main logic. +These are used in the pytest hooks that are in __init__.py. """ import collections -import json import os from typing import Any, Generator import colorama import networkx -from _pytest.nodes import Item +from pytest import Function, Item + +from agbenchmark.challenges.base import BaseChallenge from .constants import MARKER_KWARG_DEPENDENCIES, MARKER_NAME from .graphs import graph_interactive_network @@ -38,7 +39,8 @@ class TestResult(object): ) if result.when in self.results: raise AttributeError( - f"Received multiple results for step {result.when} of test {self.nodeid}" + f"Received multiple results for step {result.when} " + f"of test {self.nodeid}" ) self.results[result.when] = result.outcome @@ -66,7 +68,7 @@ class TestDependencies(object): for dep in marker.kwargs.get(MARKER_KWARG_DEPENDENCIES, []) ] for dependency in dependencies: - # If the name is not known, try to make it absolute (ie file::[class::]method) + # If the name is not known, try to make it absolute (file::[class::]method) if dependency not in manager.name_to_nodeids: absolute_dependency = get_absolute_nodeid(dependency, self.nodeid) if absolute_dependency in manager.name_to_nodeids: @@ -86,20 +88,20 @@ class DependencyManager(object): def __init__(self) -> None: """Create a new DependencyManager.""" self.options: dict[str, Any] = {} - self._items: list[Item] | None = None + self._items: list[Function] | None = None self._name_to_nodeids: Any = None self._nodeid_to_item: Any = None self._results: Any = None @property - def items(self) -> list[Item]: + def items(self) -> list[Function]: """The collected tests that are managed by this instance.""" if self._items is None: raise AttributeError("The items attribute has not been set yet") return self._items @items.setter - def items(self, items: list[Item]) -> None: + def items(self, items: list[Function]) -> None: if self._items is not None: raise AttributeError("The items attribute has already been set") self._items = items @@ -125,7 +127,8 @@ class DependencyManager(object): for item in items: nodeid = clean_nodeid(item.nodeid) # Process the dependencies of this test - # This uses the mappings created in the previous loop, and can thus not be merged into that loop + # This uses the mappings created in the previous loop, + # and can thus not be merged into that loop self._dependencies[nodeid] = TestDependencies(item, self) @property @@ -135,7 +138,7 @@ class DependencyManager(object): return self._name_to_nodeids @property - def nodeid_to_item(self) -> dict[str, Item]: + def nodeid_to_item(self) -> dict[str, Function]: """A mapping from node ids to test items.""" assert self.items is not None return self._nodeid_to_item @@ -194,7 +197,9 @@ class DependencyManager(object): @property def sorted_items(self) -> Generator: - """Get a sorted list of tests where all tests are sorted after their dependencies.""" + """ + Get a sorted list of tests where all tests are sorted after their dependencies. + """ # Build a directed graph for sorting build_skill_tree = os.getenv("BUILD_SKILL_TREE") BUILD_SKILL_TREE = ( @@ -202,8 +207,8 @@ class DependencyManager(object): ) dag = networkx.DiGraph() - # Insert all items as nodes, to prevent items that have no dependencies and are not dependencies themselves from - # being lost + # Insert all items as nodes, to prevent items that have no dependencies + # and are not dependencies themselves from being lost dag.add_nodes_from(self.items) # Insert edges for all the dependencies @@ -214,11 +219,8 @@ class DependencyManager(object): labels = {} for item in self.items: - try: - with open(item.cls.CHALLENGE_LOCATION) as f: - data = json.load(f) - except: - data = {} + assert item.cls and issubclass(item.cls, BaseChallenge) + data = item.cls.info.dict() node_name = get_name(item) data["name"] = node_name diff --git a/benchmark/agbenchmark/utils/dependencies/util.py b/benchmark/agbenchmark/utils/dependencies/util.py index f7f4664ece..417432bb2d 100644 --- a/benchmark/agbenchmark/utils/dependencies/util.py +++ b/benchmark/agbenchmark/utils/dependencies/util.py @@ -38,7 +38,8 @@ def strip_nodeid_parameters(nodeid: str) -> str: def get_absolute_nodeid(nodeid: str, scope: str) -> str: """ - Transform a possibly relative node id to an absolute one using the scope in which it is used. + Transform a possibly relative node id to an absolute one + using the scope in which it is used. >>> scope = 'test_file.py::TestClass::test' >>> get_absolute_nodeid('test2', scope) @@ -49,7 +50,7 @@ def get_absolute_nodeid(nodeid: str, scope: str) -> str: 'test_file2.py::TestClass2::test2' """ parts = nodeid.split("::") - # Completely relative (test_name), so add the full current scope (either file::class or file) + # Completely relative (test_name): add the full current scope (file::class or file) if len(parts) == 1: base_nodeid = scope.rsplit("::", 1)[0] nodeid = f"{base_nodeid}::{nodeid}" diff --git a/benchmark/agbenchmark/utils/get_data_from_helicone.py b/benchmark/agbenchmark/utils/get_data_from_helicone.py index dabb2c8b02..60b76c6bff 100644 --- a/benchmark/agbenchmark/utils/get_data_from_helicone.py +++ b/benchmark/agbenchmark/utils/get_data_from_helicone.py @@ -15,7 +15,8 @@ def get_data_from_helicone(challenge: str) -> Optional[float]: # Define the endpoint of your GraphQL server url = "https://www.helicone.ai/api/graphql" - # Set the headers, usually you'd need to set the content type and possibly an authorization token + # Set the headers, usually you'd need to set the content type + # and possibly an authorization token headers = {"authorization": f"Bearer {os.environ.get('HELICONE_API_KEY')}"} # Define the query, variables, and operation name diff --git a/benchmark/agbenchmark/utils/prompts.py b/benchmark/agbenchmark/utils/prompts.py index 76c3652afe..e5ab5ca3ca 100644 --- a/benchmark/agbenchmark/utils/prompts.py +++ b/benchmark/agbenchmark/utils/prompts.py @@ -1,7 +1,18 @@ SCORING_MAP = { - "percentage": "assign a float score that will represent a percentage out of 100. Use decimal points to be even more accurate. 0 represents the worst possible generation, while 100 represents the ideal generation", - "scale": "assign an integer score from a scale of 1-10. 1 represents a really bad generation, while 10 represents an ideal generation", - "binary": "assign a binary score of either 0 or 1. 0 represents a failure, while 1 represents a success", + "percentage": ( + "assign a float score that will represent a percentage out of 100. " + "Use decimal points to be even more accurate. " + "0 represents the worst possible generation, " + "while 100 represents the ideal generation" + ), + "scale": ( + "assign an integer score from a scale of 1-10. " + "1 represents a really bad generation, while 10 represents an ideal generation" + ), + "binary": ( + "assign a binary score of either 0 or 1. " + "0 represents a failure, while 1 represents a success" + ), } @@ -17,7 +28,7 @@ Here is the ideal response you're comparing to based on the task: Here is the current machine generated response to the task that you need to evaluate: {response} -""" +""" # noqa: E501 RUBRIC_PROMPT = """Ignore previous directions. You are now an expert at evaluating machine generated responses to given tasks. In order to score the generated texts you will {scoring}. Make sure to factor in rubric into your thinking, deliberation, and final result regarding scoring. Return nothing but a float score. @@ -31,7 +42,7 @@ Use the below rubric to guide your thinking about scoring: Here is the current machine generated response to the task that you need to evaluate: {response} -""" +""" # noqa: E501 QUESTION_PROMPT = """Ignore previous directions. You are now an expert at evaluating machine generated responses to given tasks. In order to score the generated texts you will {scoring}. Make sure to think about whether the generated response answers the question well in order to score accurately. Return nothing but a float score. @@ -45,12 +56,12 @@ Here is a question that checks if the task was completed correctly: Here is the current machine generated response to the task that you need to evaluate: {response} -""" +""" # noqa: E501 FEW_SHOT_EXAMPLES = """Here are some examples of how to score a machine generated response based on the above: {examples} -""" +""" # noqa: E501 CUSTOM_PROMPT = """{custom} {scoring} diff --git a/benchmark/agbenchmark/utils/utils.py b/benchmark/agbenchmark/utils/utils.py index 0f0ad56d9b..52d679e39e 100644 --- a/benchmark/agbenchmark/utils/utils.py +++ b/benchmark/agbenchmark/utils/utils.py @@ -202,11 +202,15 @@ def sorted_by_enum_index( sortable: Iterable[T], enum: type[Enum], *, - key: Callable[[T], Enum | None] = lambda x: x, # type: ignore + key: Optional[Callable[[T], Enum | None]] = None, reverse: bool = False, ) -> list[T]: return sorted( sortable, - key=lambda x: enum._member_names_.index(e.name) if (e := key(x)) else 420e3, + key=lambda x: ( + enum._member_names_.index(e.name) # type: ignore + if (e := key(x) if key else x) + else 420e3 + ), reverse=reverse, ) diff --git a/benchmark/mypy.ini b/benchmark/mypy.ini deleted file mode 100644 index 1b1cd5403f..0000000000 --- a/benchmark/mypy.ini +++ /dev/null @@ -1,13 +0,0 @@ -[mypy] -namespace_packages = True -follow_imports = skip -check_untyped_defs = True -disallow_untyped_defs = True -exclude = ^(agbenchmark/challenges/|agent/|venv|venv-dev) -ignore_missing_imports = True - -[mypy-agbenchmark.utils.data_types.*] -ignore_errors = True - -[mypy-numpy.*] -ignore_errors = True diff --git a/benchmark/poetry.lock b/benchmark/poetry.lock index 70bef01f63..d0a416f9b2 100644 --- a/benchmark/poetry.lock +++ b/benchmark/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "agent-protocol-client" @@ -197,63 +197,49 @@ tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] -[[package]] -name = "autoflake" -version = "1.7.8" -description = "Removes unused imports and unused variables" -optional = false -python-versions = ">=3.7" -files = [ - {file = "autoflake-1.7.8-py3-none-any.whl", hash = "sha256:46373ef69b6714f5064c923bb28bd797c4f8a9497f557d87fc36665c6d956b39"}, - {file = "autoflake-1.7.8.tar.gz", hash = "sha256:e7e46372dee46fa1c97acf310d99d922b63d369718a270809d7c278d34a194cf"}, -] - -[package.dependencies] -pyflakes = ">=1.1.0,<3" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - [[package]] name = "black" -version = "22.3.0" +version = "23.12.1" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.8" files = [ - {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, - {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, - {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, - {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, - {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, - {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, - {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, - {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, - {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, - {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, - {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, - {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, - {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, - {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, - {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, - {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, - {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, - {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, - {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -558,6 +544,73 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pill test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +[[package]] +name = "coverage" +version = "7.5.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, + {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, + {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, + {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, + {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, + {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, + {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "cycler" version = "0.12.1" @@ -671,19 +724,19 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "3.9.2" +version = "7.0.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.8.1" files = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, ] [package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "fonttools" @@ -1376,13 +1429,13 @@ traitlets = "*" [[package]] name = "mccabe" -version = "0.6.1" +version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] @@ -1973,13 +2026,13 @@ pyasn1 = ">=0.4.6,<0.6.0" [[package]] name = "pycodestyle" -version = "2.7.0" +version = "2.11.1" description = "Python style guide checker" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, ] [[package]] @@ -2047,13 +2100,13 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pyflakes" -version = "2.3.1" +version = "3.2.0" description = "passive checker of Python programs" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] @@ -2085,6 +2138,24 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyright" +version = "1.1.364" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.364-py3-none-any.whl", hash = "sha256:865f1e02873c5dc7427c95acf53659a118574010e6fb364e27e47ec5c46a9f26"}, + {file = "pyright-1.1.364.tar.gz", hash = "sha256:612a2106a4078ec57efc22b5620729e9bdf4a3c17caba013b534bd33f7d08e5a"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" + +[package.extras] +all = ["twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] + [[package]] name = "pysocks" version = "1.7.1" @@ -2137,6 +2208,24 @@ pytest = ">=7.0.0" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -2774,4 +2863,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "6eefdbbefb500de627cac39eb6eb1fdcecab76dd4c3599cf08ef6dc647cf71c9" +content-hash = "4a980e6d8f54a2f7f6a3c55d4f40ac3a4b27b5ac6573dd2a39e11213a4b126dd" diff --git a/benchmark/pyproject.toml b/benchmark/pyproject.toml index 231ad0974e..240ed064b8 100644 --- a/benchmark/pyproject.toml +++ b/benchmark/pyproject.toml @@ -37,59 +37,49 @@ click-default-group = "^1.2.4" tabulate = "^0.9.0" [tool.poetry.group.dev.dependencies] -flake8 = "^3.9.2" -isort = "^5.9.3" -black = "22.3" -autoflake = "^1.4" +black = "^23.12.1" +flake8 = "^7.0.0" +isort = "^5.13.1" +pyright = "^1.1.364" pandas = "^2.0.3" gspread = "^5.10.0" oauth2client = "^4.1.3" pre-commit = "^3.3.3" +pytest-cov = "^5.0.0" + +[tool.poetry.scripts] +agbenchmark = "agbenchmark.__main__:cli" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.pytest.ini_options] -minversion = "6.0" -addopts = "-ra -q" -testpaths = [ - "tests", "agbenchmark", -] -asyncio_mode = "auto" -markers = [ - "interface", - "code", - "memory", - "iterate", - "adaptability", - "safety", - "content_gen", - "product_advisor" -] -filterwarnings = [ - "ignore::pytest.PytestAssertRewriteWarning", - "ignore::matplotlib.MatplotlibDeprecationWarning" -] - [tool.black] line-length = 88 target-version = ['py310'] include = '\.pyi?$' -packages = ["autogpt"] -extend-exclude = '(/dist|/.venv|/venv|/build|/agent|agbenchmark/challenges)/' + [tool.isort] profile = "black" -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -ensure_newline_before_comments = true -line_length = 88 -sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] -skip_glob = [".tox", "__pycache__", "*.pyc", "venv*/*", "reports", "venv", "env", "node_modules", ".env", ".venv", "dist", "agent/*", "agbenchmark/challenges/*"] +skip_glob = ["reports"] -[tool.poetry.scripts] -agbenchmark = "agbenchmark.__main__:cli" + +[tool.pyright] +pythonVersion = "3.10" +exclude = [ + "notebooks/**", + "reports/**", + "**/node_modules", + "**/__pycache__", + "**/.*", +] +ignore = [ + "../forge/**" +] + + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/benchmark/reports/format.py b/benchmark/reports/format.py index 8a9a872ea9..8a43ea1e37 100755 --- a/benchmark/reports/format.py +++ b/benchmark/reports/format.py @@ -17,7 +17,7 @@ def print_markdown_report(report_json_file: str): report = Report.parse_file(report_json_file) # Header and metadata - click.echo(f"# Benchmark Report") + click.echo("# Benchmark Report") click.echo(f"- ⌛ **Run time:** `{report.metrics.run_time}`") click.echo( f" - **Started at:** `{report.benchmark_start_time[:16].replace('T', '` `')}`" diff --git a/benchmark/tests/test_benchmark_workflow.py b/benchmark/tests/test_benchmark_workflow.py index ca3eec88bb..971c3a0e68 100644 --- a/benchmark/tests/test_benchmark_workflow.py +++ b/benchmark/tests/test_benchmark_workflow.py @@ -1,11 +1,16 @@ +import datetime +import time + import pytest import requests URL_BENCHMARK = "http://localhost:8080/ap/v1" URL_AGENT = "http://localhost:8000/ap/v1" -import datetime -import time +try: + response = requests.get(f"{URL_AGENT}/agent/tasks") +except requests.exceptions.ConnectionError: + pytest.skip("No agent available to test against", allow_module_level=True) @pytest.mark.parametrize( @@ -20,7 +25,8 @@ import time ), ( "f219f3d3-a41b-45a9-a3d0-389832086ee8", - "Read the file called file_to_read.txt and write its content to a file called output.txt", + "Read the file called file_to_read.txt " + "and write its content to a file called output.txt", 1, "ReadFile", False, @@ -28,7 +34,11 @@ import time ], ) def test_entire_workflow( - eval_id, input_text, expected_artifact_length, test_name, should_be_successful + eval_id: str, + input_text: str, + expected_artifact_length: int, + test_name: str, + should_be_successful: bool, ): task_request = {"eval_id": eval_id, "input": input_text} response = requests.get(f"{URL_AGENT}/agent/tasks") @@ -64,7 +74,7 @@ def test_entire_workflow( ) assert step_response.status_code == 200 step_response = step_response.json() - assert step_response["is_last"] == True # Assuming is_last is always True + assert step_response["is_last"] is True # Assuming is_last is always True eval_response = requests.post( URL_BENCHMARK + "/agent/tasks/" + task_response_benchmark_id + "/evaluations", diff --git a/cli.py b/cli.py index ecf603825a..59a8587bd9 100644 --- a/cli.py +++ b/cli.py @@ -94,14 +94,14 @@ def create(agent_name: str): ) return try: - new_agent_dir = f"./autogpts/{agent_name}" + new_agent_dir = f"./agents/{agent_name}" new_agent_name = f"{agent_name.lower()}.json" if not os.path.exists(new_agent_dir): - shutil.copytree("./autogpts/forge", new_agent_dir) + shutil.copytree("./forge", new_agent_dir) click.echo( click.style( - f"🎉 New agent '{agent_name}' created. The code for your new agent is in: autogpts/{agent_name}", + f"🎉 New agent '{agent_name}' created. The code for your new agent is in: agents/{agent_name}", fg="green", ) ) @@ -129,7 +129,12 @@ def start(agent_name: str, no_setup: bool): import subprocess script_dir = os.path.dirname(os.path.realpath(__file__)) - agent_dir = os.path.join(script_dir, f"autogpts/{agent_name}") + agent_dir = os.path.join( + script_dir, + f"agents/{agent_name}" + if agent_name not in ["autogpt", "forge"] + else agent_name, + ) run_command = os.path.join(agent_dir, "run") run_bench_command = os.path.join(agent_dir, "run_benchmark") if ( @@ -203,12 +208,14 @@ def list(): import os try: - agents_dir = "./autogpts" + agents_dir = "./agents" agents_list = [ d for d in os.listdir(agents_dir) if os.path.isdir(os.path.join(agents_dir, d)) ] + if os.path.isdir("./autogpt"): + agents_list.append("autogpt") if agents_list: click.echo(click.style("Available agents: 🤖", fg="green")) for agent in agents_list: @@ -216,7 +223,7 @@ def list(): else: click.echo(click.style("No agents found 😞", fg="red")) except FileNotFoundError: - click.echo(click.style("The autogpts directory does not exist 😢", fg="red")) + click.echo(click.style("The agents directory does not exist 😢", fg="red")) except Exception as e: click.echo(click.style(f"An error occurred: {e} 😢", fg="red")) @@ -240,7 +247,12 @@ def start(agent_name, subprocess_args): import subprocess script_dir = os.path.dirname(os.path.realpath(__file__)) - agent_dir = os.path.join(script_dir, f"autogpts/{agent_name}") + agent_dir = os.path.join( + script_dir, + f"agents/{agent_name}" + if agent_name not in ["autogpt", "forge"] + else agent_name, + ) benchmark_script = os.path.join(agent_dir, "run_benchmark") if os.path.exists(agent_dir) and os.path.isfile(benchmark_script): os.chdir(agent_dir) diff --git a/docs/content/AutoGPT/configuration/options.md b/docs/content/AutoGPT/configuration/options.md index 17602102ba..0072dd747d 100644 --- a/docs/content/AutoGPT/configuration/options.md +++ b/docs/content/AutoGPT/configuration/options.md @@ -4,7 +4,6 @@ Configuration is controlled through the `Config` object. You can set configurati ## Environment Variables -- `AI_SETTINGS_FILE`: Location of the AI Settings file relative to the AutoGPT root directory. Default: ai_settings.yaml - `AUDIO_TO_TEXT_PROVIDER`: Audio To Text Provider. Only option currently is `huggingface`. Default: huggingface - `AUTHORISE_COMMAND_KEY`: Key response accepted when authorising commands. Default: y - `ANTHROPIC_API_KEY`: Set this if you want to use Anthropic models with AutoGPT @@ -23,21 +22,16 @@ Configuration is controlled through the `Config` object. You can set configurati - `GITHUB_USERNAME`: GitHub Username. Optional. - `GOOGLE_API_KEY`: Google API key. Optional. - `GOOGLE_CUSTOM_SEARCH_ENGINE_ID`: [Google custom search engine ID](https://programmablesearchengine.google.com/controlpanel/all). Optional. +- `GROQ_API_KEY`: Set this if you want to use Groq models with AutoGPT - `HEADLESS_BROWSER`: Use a headless browser while AutoGPT uses a web browser. Setting to `False` will allow you to see AutoGPT operate the browser. Default: True - `HUGGINGFACE_API_TOKEN`: HuggingFace API, to be used for both image generation and audio to text. Optional. - `HUGGINGFACE_AUDIO_TO_TEXT_MODEL`: HuggingFace audio to text model. Default: CompVis/stable-diffusion-v1-4 - `HUGGINGFACE_IMAGE_MODEL`: HuggingFace model to use for image generation. Default: CompVis/stable-diffusion-v1-4 - `IMAGE_PROVIDER`: Image provider. Options are `dalle`, `huggingface`, and `sdwebui`. Default: dalle - `IMAGE_SIZE`: Default size of image to generate. Default: 256 -- `MEMORY_BACKEND`: Memory back-end to use. Currently `json_file` is the only supported and enabled backend. Default: json_file -- `MEMORY_INDEX`: Value used in the Memory backend for scoping, naming, or indexing. Default: auto-gpt - `OPENAI_API_KEY`: *REQUIRED*- Your [OpenAI API Key](https://platform.openai.com/account/api-keys). - `OPENAI_ORGANIZATION`: Organization ID in OpenAI. Optional. - `PLAIN_OUTPUT`: Plain output, which disables the spinner. Default: False -- `PROMPT_SETTINGS_FILE`: Location of the Prompt Settings file relative to the AutoGPT root directory. Default: prompt_settings.yaml -- `REDIS_HOST`: Redis Host. Default: localhost -- `REDIS_PASSWORD`: Redis Password. Optional. Default: -- `REDIS_PORT`: Redis Port. Default: 6379 - `RESTRICT_TO_WORKSPACE`: The restrict file reading and writing to the workspace directory. Default: True - `SD_WEBUI_AUTH`: Stable Diffusion Web UI username:password pair. Optional. - `SD_WEBUI_URL`: Stable Diffusion Web UI URL. Default: http://localhost:7860 diff --git a/docs/content/AutoGPT/index.md b/docs/content/AutoGPT/index.md index c53589baa8..de993b0348 100644 --- a/docs/content/AutoGPT/index.md +++ b/docs/content/AutoGPT/index.md @@ -4,9 +4,9 @@  |  [💻 **User guide**](./usage.md)  |  -[🐙 **GitHub**](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt) +[🐙 **GitHub**](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt) -**Location:** `autogpts/autogpt/` in the GitHub repo +**Location:** `autogpt/` in the GitHub repo AutoGPT was conceived when OpenAI published their GPT-4 model accompanied by a paper outlining the advanced reasoning and task-solving abilities of the model. The concept diff --git a/docs/content/AutoGPT/setup/docker.md b/docs/content/AutoGPT/setup/docker.md index e730486964..4e10ae29de 100644 --- a/docs/content/AutoGPT/setup/docker.md +++ b/docs/content/AutoGPT/setup/docker.md @@ -47,12 +47,6 @@ #- type: bind # source: ./azure.yaml # target: /app/azure.yaml - #- type: bind - # source: ./ai_settings.yaml - # target: /app/ai_settings.yaml - #- type: bind - # source: ./prompt_settings.yaml - # target: /app/prompt_settings.yaml ``` @@ -77,18 +71,13 @@ - ./logs:/app/logs ## uncomment following lines if you want to make use of these files ## you must have them existing in the same folder as this docker-compose.yml - #- type: bind - # source: ./ai_settings.yaml - # target: /app/ai_settings.yaml - #- type: bind - # source: ./prompt_settings.yaml - # target: /app/prompt_settings.yaml ``` 4. Download [`.env.template`][.env.template] and save it as `.env` in the AutoGPT folder. -5. Follow the [configuration](#configuration) steps. +5. Follow the standard [configuration instructions](../index.md#completing-the-setup), + from step 3 onwards and excluding `poetry install` steps. 6. Pull the latest image from [Docker Hub] ```shell @@ -99,54 +88,9 @@ AutoGPT uses a browser in headless mode by default: `HEADLESS_BROWSER=True`. Please do not change this setting in combination with Docker, or AutoGPT will crash. -[.env.template]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/.env.template +[.env.template]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/.env.template [Docker Hub]: https://hub.docker.com/r/significantgravitas/auto-gpt -## Configuration - -1. Open the `.env` file in a text editor. This file may - be hidden by default in some operating systems due to the dot prefix. To reveal - hidden files, follow the instructions for your specific operating system: - [Windows][show hidden files/Windows], [macOS][show hidden files/macOS]. -2. Find the line that says `OPENAI_API_KEY=`. -3. After the `=`, enter your unique OpenAI API Key *without any quotes or spaces*. -4. Enter any other API keys or tokens for services you would like to use. - - !!! note - To activate and adjust a setting, remove the `# ` prefix. - -5. Save and close the `.env` file. - -Templates for the optional extra configuration files (e.g. `prompt_settings.yml`) can be -found in the [repository]. - -!!! info "Using a GPT Azure-instance" - If you want to use GPT on an Azure instance, set `USE_AZURE` to `True` and - make an Azure configuration file: - - - Rename `azure.yaml.template` to `azure.yaml` and provide the relevant `azure_api_base`, `azure_api_version` and all the deployment IDs for the relevant models in the `azure_model_map` section. - - Example: - - ```yaml - # Please specify all of these values as double-quoted strings - # Replace string in angled brackets (<>) to your own deployment Name - azure_model_map: - gpt-4-turbo-preview: "" - ... - ``` - - Details can be found in the [openai-python docs], and in the [Azure OpenAI docs] for the embedding model. - If you're on Windows you may need to install an [MSVC library](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). - - **Note:** Azure support has been dropped in `master`, so these instructions will only work with v0.4.7 (or earlier). - -[repository]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt -[show hidden files/Windows]: https://support.microsoft.com/en-us/windows/view-hidden-files-and-folders-in-windows-97fbc472-c603-9d90-91d0-1166d1d9f4b5 -[show hidden files/macOS]: https://www.pcmag.com/how-to/how-to-access-your-macs-hidden-files -[openai-python docs]: https://github.com/openai/openai-python#microsoft-azure-endpoints -[Azure OpenAI docs]: https://learn.microsoft.com/en-us/azure/cognitive-services/openai/tutorials/embeddings?tabs=command-line - ## Developer Setup !!! tip @@ -154,7 +98,8 @@ found in the [repository]. changes to the codebase. 1. Copy `.env.template` to `.env`. -2. Follow the standard [configuration](#configuration) steps above. +2. Follow the standard [configuration instructions](../index.md#completing-the-setup), + from step 3 onwards and excluding `poetry install` steps. ## Running AutoGPT with Docker diff --git a/docs/content/AutoGPT/setup/index.md b/docs/content/AutoGPT/setup/index.md index 540c7f116a..43aba64747 100644 --- a/docs/content/AutoGPT/setup/index.md +++ b/docs/content/AutoGPT/setup/index.md @@ -28,27 +28,6 @@ - [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/) -### 🗝️ Getting an OpenAI API key - -Get your OpenAI API key from: -[https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys). - -!!! attention - To use the OpenAI API with AutoGPT, we strongly recommend **setting up billing** - (AKA paid account). Free accounts are [limited][openai/api limits] to 3 API calls per - minute, which can cause the application to crash. - - You can set up a paid account at [Manage account > Billing > Overview](https://platform.openai.com/account/billing/overview). - -[openai/api limits]: https://platform.openai.com/docs/guides/rate-limits/free-tier-rate-limits - -!!! important - It's highly recommended that you keep track of your API costs on [the Usage page](https://platform.openai.com/account/usage). - You can also set limits on how much you spend on [the Usage limits page](https://platform.openai.com/account/billing/limits). - -![For OpenAI API key to work, set up paid account at OpenAI API > Billing](../../imgs/openai-api-key-billing-paid-account.png) - - ## Setting up AutoGPT ### Getting AutoGPT @@ -71,7 +50,7 @@ Since we don't ship AutoGPT as a desktop application, you'll need to download th ### Completing the Setup Once you have cloned or downloaded the project, you can find the AutoGPT Agent in the -`autogpts/autogpt/` folder. In this folder: +`autogpt/` folder. In this folder: 1. Find the file named `.env.template`. This file may be hidden by default in some operating systems due to the dot prefix. To reveal @@ -83,10 +62,49 @@ Once you have cloned or downloaded the project, you can find the AutoGPT Agent i cp .env.template .env ``` 3. Open the `.env` file in a text editor. -4. Find the line that says `OPENAI_API_KEY=`. -5. Insert your OpenAI API Key directly after = without quotes or spaces.. - ```yaml - OPENAI_API_KEY=sk-qwertykeys123456 +4. Set API keys for the LLM providers that you want to use: see [below](#setting-up-llm-providers). +5. Enter any other API keys or tokens for services you would like to use. + + !!! note + To activate and adjust a setting, remove the `# ` prefix. + +6. Save and close the `.env` file. +7. _Optional: run `poetry install` to install all required dependencies._ The + application also checks for and installs any required dependencies when it starts. + +You should now be able to explore the CLI (`./autogpt.sh --help`) and run the application. + +See the [user guide](../usage.md) for further instructions. + +[show hidden files/Windows]: https://support.microsoft.com/en-us/windows/view-hidden-files-and-folders-in-windows-97fbc472-c603-9d90-91d0-1166d1d9f4b5 +[show hidden files/macOS]: https://www.pcmag.com/how-to/how-to-access-your-macs-hidden-files + + +## Setting up LLM providers + +You can use AutoGPT with any of the following LLM providers. +Each of them comes with its own setup instructions. + +AutoGPT was originally built on top of OpenAI's GPT-4, but now you can get +similar and interesting results using other models/providers too. +If you don't know which to choose, you can safely go with OpenAI*. + +* subject to change + +### OpenAI + +!!! attention + To use AutoGPT with GPT-4 (recommended), you need to set up a paid OpenAI account + with some money in it. Please refer to OpenAI for further instructions ([link][openai/help-gpt-4-access]). + Free accounts are [limited][openai/api-limits] to GPT-3.5 with only 3 requests per minute. + +1. Make sure you have a paid account with some credits set up: [Settings > Organization > Billing][openai/billing] +1. Get your OpenAI API key from: [API keys][openai/api-keys] +2. Open `.env` +3. Find the line that says `OPENAI_API_KEY=` +4. Insert your OpenAI API Key directly after = without quotes or spaces: + ```ini + OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` !!! info "Using a GPT Azure-instance" @@ -97,31 +115,78 @@ Once you have cloned or downloaded the project, you can find the AutoGPT Agent i `azure_api_base`, `azure_api_version` and deployment IDs for the models that you want to use. - E.g. if you want to use `gpt-3.5-turbo-16k` and `gpt-4-0314`: + E.g. if you want to use `gpt-3.5-turbo` and `gpt-4-turbo`: ```yaml # Please specify all of these values as double-quoted strings # Replace string in angled brackets (<>) to your own deployment Name azure_model_map: - gpt-3.5-turbo-16k: "" + gpt-3.5-turbo: "" + gpt-4-turbo: "" ... ``` - Details can be found in the [openai-python docs], and in the [Azure OpenAI docs] for the embedding model. + Details can be found in the [openai/python-sdk/azure], and in the [Azure OpenAI docs] for the embedding model. If you're on Windows you may need to install an [MSVC library](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). -6. Enter any other API keys or tokens for services you would like to use. +!!! important + Keep an eye on your API costs on [the Usage page][openai/usage]. - !!! note - To activate and adjust a setting, remove the `# ` prefix. -7. Save and close the `.env` file. -8. _Optional: run `poetry install` to install all required dependencies._ The - application also checks for and installs any required dependencies when it starts. +[openai/api-keys]: https://platform.openai.com/account/api-keys +[openai/billing]: https://platform.openai.com/account/billing/overview +[openai/usage]: https://platform.openai.com/account/usage +[openai/api-limits]: https://platform.openai.com/docs/guides/rate-limits/free-tier-rate-limits +[openai/help-gpt-4-access]: https://help.openai.com/en/articles/7102672-how-can-i-access-gpt-4-gpt-4-turbo-and-gpt-4o#h_9bddcd317c +[openai/python-sdk/azure]: https://github.com/openai/openai-python?tab=readme-ov-file#microsoft-azure-openai -You should now be able to explore the CLI (`./autogpt.sh --help`) and run the application. -See the [user guide](../usage.md) for further instructions. +### Anthropic -[show hidden files/Windows]: https://support.microsoft.com/en-us/windows/view-hidden-files-and-folders-in-windows-97fbc472-c603-9d90-91d0-1166d1d9f4b5 -[show hidden files/macOS]: https://www.pcmag.com/how-to/how-to-access-your-macs-hidden-files +1. Make sure you have credits in your account: [Settings > Plans & billing][anthropic/billing] +2. Get your Anthropic API key from [Settings > API keys][anthropic/api-keys] +3. Open `.env` +4. Find the line that says `ANTHROPIC_API_KEY=` +5. Insert your Anthropic API Key directly after = without quotes or spaces: + ```ini + ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ``` +6. Set `SMART_LLM` and/or `FAST_LLM` to the Claude 3 model you want to use. + See Anthropic's [models overview][anthropic/models] for info on the available models. + Example: + ```ini + SMART_LLM=claude-3-opus-20240229 + ``` + +!!! important + Keep an eye on your API costs on [the Usage page][anthropic/usage]. + +[anthropic/api-keys]: https://console.anthropic.com/settings/keys +[anthropic/billing]: https://console.anthropic.com/settings/plans +[anthropic/usage]: https://console.anthropic.com/settings/usage +[anthropic/models]: https://docs.anthropic.com/en/docs/models-overview + + +### Groq + +!!! note + Although Groq is supported, its built-in function calling API isn't mature. + Any features using this API may experience degraded performance. + Let us know your experience! + +1. Get your Groq API key from [Settings > API keys][groq/api-keys] +2. Open `.env` +3. Find the line that says `GROQ_API_KEY=` +4. Insert your Anthropic API Key directly after = without quotes or spaces: + ```ini + GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ``` +5. Set `SMART_LLM` and/or `FAST_LLM` to the Groq model you want to use. + See Groq's [models overview][groq/models] for info on the available models. + Example: + ```ini + SMART_LLM=llama3-70b-8192 + ``` + +[groq/api-keys]: https://console.groq.com/keys +[groq/models]: https://console.groq.com/docs/models diff --git a/docs/content/AutoGPT/usage.md b/docs/content/AutoGPT/usage.md index 5a19dd9cb0..2a3bdd0c17 100644 --- a/docs/content/AutoGPT/usage.md +++ b/docs/content/AutoGPT/usage.md @@ -1,7 +1,7 @@ # AutoGPT Agent User Guide !!! note - This guide assumes you are in the `autogpts/autogpt` folder, where the AutoGPT Agent + This guide assumes you are in the `autogpt` folder, where the AutoGPT Agent is located. ## Command Line Interface @@ -54,11 +54,6 @@ Options: -c, --continuous Enable Continuous Mode -y, --skip-reprompt Skips the re-prompting messages at the beginning of the script - -C, --ai-settings FILE Specifies which ai_settings.yaml file to - use, relative to the AutoGPT root directory. - Will also automatically skip the re-prompt. - -P, --prompt-settings FILE Specifies which prompt_settings.yaml file to - use. -l, --continuous-limit INTEGER Defines the number of times to run in continuous mode --speak Enable Speak Mode @@ -130,8 +125,6 @@ Usage: python -m autogpt serve [OPTIONS] agent for every task. Options: - -P, --prompt-settings FILE Specifies which prompt_settings.yaml file to - use. --debug Enable Debug Mode --gpt3only Enable GPT3.5 Only Mode --gpt4only Enable GPT4 Only Mode @@ -176,7 +169,7 @@ Here are some common arguments you can use when running AutoGPT: There are shorthands for some of these flags, for example `-P` for `--prompt-settings`. Use `./autogpt.sh --help` for more information. -[.env.template]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/.env.template +[.env.template]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/.env.template ## Agent State [agent state]: #agent-state diff --git a/docs/content/challenges/building_challenges.md b/docs/content/challenges/building_challenges.md index 74b11323fd..d63880868b 100644 --- a/docs/content/challenges/building_challenges.md +++ b/docs/content/challenges/building_challenges.md @@ -27,13 +27,13 @@ Output => Artifact (files, image, code, etc, etc...) ## Defining your Agent -Go to https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpts/autogpt/tests/integration/agent_factory.py +Go to https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt/tests/integration/agent_factory.py Create your agent fixture. ```python def kubernetes_agent( - agent_test_config, memory_json_file, workspace: Workspace + agent_test_config, workspace: Workspace ): # Please choose the commands your agent will need to beat the challenges, the full list is available in the main.py # (we 're working on a better way to design this, for now you have to look at main.py) @@ -54,7 +54,6 @@ def kubernetes_agent( system_prompt = ai_profile.construct_full_prompt() agent_test_config.set_continuous_mode(False) agent = Agent( - memory=memory_json_file, command_registry=command_registry, config=ai_profile, next_action_count=0, diff --git a/docs/content/forge/components/agents.md b/docs/content/forge/components/agents.md index 7d3808a149..fc32b1ad84 120000 --- a/docs/content/forge/components/agents.md +++ b/docs/content/forge/components/agents.md @@ -1 +1 @@ -../../../../autogpts/autogpt/autogpt/agents/README.md \ No newline at end of file +../../../../autogpt/autogpt/agents/README.md \ No newline at end of file diff --git a/docs/content/forge/components/components.md b/docs/content/forge/components/components.md index 6dcee2945b..e212342279 120000 --- a/docs/content/forge/components/components.md +++ b/docs/content/forge/components/components.md @@ -1 +1 @@ -../../../../autogpts/forge/forge/components/README.md \ No newline at end of file +../../../../forge/forge/components/README.md \ No newline at end of file diff --git a/docs/content/forge/components/creating-components.md b/docs/content/forge/components/creating-components.md index 2eed5c6832..033561c6ca 100644 --- a/docs/content/forge/components/creating-components.md +++ b/docs/content/forge/components/creating-components.md @@ -93,12 +93,13 @@ To learn more about commands see [🛠️ Commands](./commands.md). After components provided all necessary data, the agent needs to build the final prompt that will be send to a llm. Currently, `PromptStrategy` (*not* a protocol) is responsible for building the final prompt. -If you want to change the way the prompt is built, you need to create a new `PromptStrategy` class, and then call relavant methods in your agent class. -You can have a look at the default strategy used by the AutoGPT Agent: [OneShotAgentPromptStrategy](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/autogpt/agents/prompt_strategies/one_shot.py), and how it's used in the [Agent](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/autogpt/agents/agent.py) (search for `self.prompt_strategy`). + +If you want to change the way the prompt is built, you need to create a new `PromptStrategy` class, and then call relevant methods in your agent class. +You can have a look at the default strategy used by the AutoGPT Agent: [OneShotAgentPromptStrategy](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/autogpt/agents/prompt_strategies/one_shot.py), and how it's used in the [Agent](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/autogpt/agents/agent.py) (search for `self.prompt_strategy`). ## Example `UserInteractionComponent` -Let's create a slighlty simplified version of the component that is used by the built-in agent. +Let's create a slightly simplified version of the component that is used by the built-in agent. It gives an ability for the agent to ask user for input in the terminal. 1. Create a class for the component that inherits from `CommandProvider`. @@ -202,7 +203,7 @@ class MyAgent(Agent): def __init__( self, settings: AgentSettings, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, file_storage: FileStorage, legacy_config: Config, ): @@ -219,7 +220,7 @@ class MyAgent(Agent): def __init__( self, settings: AgentSettings, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, file_storage: FileStorage, legacy_config: Config, ): @@ -233,7 +234,7 @@ class MyAgent(Agent): ## Learn more -The best place to see more examples is to look at the built-in components in the [autogpt/components](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/autogpt/components/) and [autogpt/commands](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpts/autogpt/autogpt/commands/) directories. +The best place to see more examples is to look at the built-in components in the [autogpt/components](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/autogpt/components/) and [autogpt/commands](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt/autogpt/commands/) directories. Guide on how to extend the built-in agent and build your own: [🤖 Agents](./agents.md) Order of some components matters, see [🧩 Components](./components.md) to learn more about components and how they can be customized. diff --git a/docs/content/index.md b/docs/content/index.md index d027ca4afb..ad37ffef64 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -95,7 +95,7 @@ Commands: Common commands: * `./run agent start autogpt` – [runs](./AutoGPT/usage.md#serve-agent-protocol-mode-with-ui) the AutoGPT agent -* `./run agent create ` – creates a new Forge-based agent project at `autogpts/` +* `./run agent create ` – creates a new Forge-based agent project at `agents/` * `./run benchmark start ` – benchmarks the specified agent --- diff --git a/autogpts/forge/.env.example b/forge/.env.example similarity index 80% rename from autogpts/forge/.env.example rename to forge/.env.example index 1c80257402..dc4bcb478b 100644 --- a/autogpts/forge/.env.example +++ b/forge/.env.example @@ -5,4 +5,3 @@ OPENAI_API_KEY=abc LOG_LEVEL=INFO DATABASE_STRING="sqlite:///agent.db" PORT=8000 -AGENT_WORKSPACE="agbenchmark_config/workspace" diff --git a/forge/.flake8 b/forge/.flake8 new file mode 100644 index 0000000000..1f66114a17 --- /dev/null +++ b/forge/.flake8 @@ -0,0 +1,11 @@ +[flake8] +max-line-length = 88 +# Ignore rules that conflict with Black code style +extend-ignore = E203, W503 +exclude = + .git, + __pycache__/, + *.pyc, + .pytest_cache/, + venv*/, + .venv/, diff --git a/autogpts/forge/.gitignore b/forge/.gitignore similarity index 97% rename from autogpts/forge/.gitignore rename to forge/.gitignore index afac6b07bc..6d1a1580fa 100644 --- a/autogpts/forge/.gitignore +++ b/forge/.gitignore @@ -4,8 +4,6 @@ autogpt/*.json *.mpeg .env azure.yaml -ai_settings.yaml -last_run_ai_settings.yaml .vscode .idea/* auto-gpt.json @@ -162,7 +160,8 @@ CURRENT_BULLETIN.md agbenchmark_config/workspace agbenchmark_config/reports -*.sqlite +*.sqlite* +*.db .agbench .agbenchmark .benchmarks @@ -170,7 +169,7 @@ agbenchmark_config/reports .pytest_cache .vscode ig_* -agent.db agbenchmark_config/updates.json agbenchmark_config/challenges_already_beaten.json agbenchmark_config/temp_folder/* +test_workspace/ diff --git a/autogpts/forge/Dockerfile b/forge/Dockerfile similarity index 100% rename from autogpts/forge/Dockerfile rename to forge/Dockerfile diff --git a/autogpts/forge/README.md b/forge/README.md similarity index 100% rename from autogpts/forge/README.md rename to forge/README.md diff --git a/autogpts/forge/agbenchmark_config/config.json b/forge/agbenchmark_config/config.json similarity index 100% rename from autogpts/forge/agbenchmark_config/config.json rename to forge/agbenchmark_config/config.json diff --git a/autogpts/autogpt/scripts/__init__.py b/forge/forge/__init__.py similarity index 100% rename from autogpts/autogpt/scripts/__init__.py rename to forge/forge/__init__.py diff --git a/forge/forge/__main__.py b/forge/forge/__main__.py new file mode 100644 index 0000000000..4df7d2deb0 --- /dev/null +++ b/forge/forge/__main__.py @@ -0,0 +1,54 @@ +import logging +import os + +import uvicorn +from dotenv import load_dotenv + +from forge.logging.config import configure_logging + +logger = logging.getLogger(__name__) + +logo = """\n\n + d8888 888 .d8888b. 8888888b. 88888888888 + d88P888 888 888 888 888 888 888 + d88P 888 888 888 888888 .d88b. 888 888 d88P 888 + d88P 888 888 888 888 d88""88b 888 88888 8888888P" 888 + d88P 888 888 888 888 888 888 888 888 888 888 + d8888888888 Y88b 888 Y88b. Y88..88P Y88b d88P 888 888 +d88P 888 "Y88888 "Y888 "Y88P" "Y8888P88 888 888 + + + 8888888888 + 888 + 888 .d88b. 888d888 .d88b. .d88b. + 888888 d88""88b 888P" d88P"88b d8P Y8b + 888 888 888 888 888 888 88888888 + 888 Y88..88P 888 Y88b 888 Y8b. + 888 "Y88P" 888 "Y88888 "Y8888 + 888 + Y8b d88P + "Y88P" v0.1.0 +\n""" + +if __name__ == "__main__": + print(logo) + port = os.getenv("PORT", 8000) + configure_logging() + logger.info(f"Agent server starting on http://localhost:{port}") + load_dotenv() + + uvicorn.run( + "forge.app:app", + host="localhost", + port=int(port), + log_level="error", + # Reload on changes to code or .env + reload=True, + reload_dirs=os.path.dirname(os.path.dirname(__file__)), + reload_excludes="*.py", # Cancel default *.py include pattern + reload_includes=[ + f"{os.path.basename(os.path.dirname(__file__))}/**/*.py", + ".*", + ".env", + ], + ) diff --git a/forge/forge/agent/__init__.py b/forge/forge/agent/__init__.py new file mode 100644 index 0000000000..87522ed53a --- /dev/null +++ b/forge/forge/agent/__init__.py @@ -0,0 +1,7 @@ +from .base import BaseAgent, BaseAgentConfiguration, BaseAgentSettings + +__all__ = [ + "BaseAgent", + "BaseAgentConfiguration", + "BaseAgentSettings", +] diff --git a/autogpts/forge/forge/sdk/agent.py b/forge/forge/agent/agent.py similarity index 56% rename from autogpts/forge/forge/sdk/agent.py rename to forge/forge/agent/agent.py index ca74607bbd..a6b2c8f78d 100644 --- a/autogpts/forge/forge/sdk/agent.py +++ b/forge/forge/agent/agent.py @@ -1,3 +1,4 @@ +import logging import os import pathlib from io import BytesIO @@ -9,12 +10,10 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse, StreamingResponse from fastapi.staticfiles import StaticFiles -from forge.utils.exceptions import NotFoundError - -from .db import AgentDB -from .forge_log import ForgeLogger -from .middlewares import AgentMiddleware -from .model import ( +from forge.agent_protocol.api_router import base_router +from forge.agent_protocol.database.db import AgentDB +from forge.agent_protocol.middlewares import AgentMiddleware +from forge.agent_protocol.models.task import ( Artifact, Step, StepRequestBody, @@ -24,14 +23,13 @@ from .model import ( TaskRequestBody, TaskStepsListResponse, ) -from .routes.agent_protocol import base_router -from .workspace import Workspace +from forge.file_storage.base import FileStorage -LOG = ForgeLogger(__name__) +logger = logging.getLogger(__name__) class Agent: - def __init__(self, database: AgentDB, workspace: Workspace): + def __init__(self, database: AgentDB, workspace: FileStorage): self.db = database self.workspace = workspace @@ -68,7 +66,7 @@ class Agent: app.include_router(router, prefix="/ap/v1") script_dir = os.path.dirname(os.path.realpath(__file__)) frontend_path = pathlib.Path( - os.path.join(script_dir, "../../../../frontend/build/web") + os.path.join(script_dir, "../../../frontend/build/web") ).resolve() if os.path.exists(frontend_path): @@ -79,8 +77,9 @@ class Agent: return RedirectResponse(url="/app/index.html", status_code=307) else: - LOG.warning( - f"Frontend not found. {frontend_path} does not exist. The frontend will not be served" + logger.warning( + f"Frontend not found. {frontend_path} does not exist. " + "The frontend will not be served." ) app.add_middleware(AgentMiddleware, agent=self) @@ -95,34 +94,25 @@ class Agent: """ Create a task for the agent. """ - try: - task = await self.db.create_task( - input=task_request.input, - additional_input=task_request.additional_input, - ) - return task - except Exception as e: - raise + task = await self.db.create_task( + input=task_request.input, + additional_input=task_request.additional_input, + ) + return task async def list_tasks(self, page: int = 1, pageSize: int = 10) -> TaskListResponse: """ List all tasks that the agent has created. """ - try: - tasks, pagination = await self.db.list_tasks(page, pageSize) - response = TaskListResponse(tasks=tasks, pagination=pagination) - return response - except Exception as e: - raise + tasks, pagination = await self.db.list_tasks(page, pageSize) + response = TaskListResponse(tasks=tasks, pagination=pagination) + return response async def get_task(self, task_id: str) -> Task: """ Get a task by ID. """ - try: - task = await self.db.get_task(task_id) - except Exception as e: - raise + task = await self.db.get_task(task_id) return task async def list_steps( @@ -131,12 +121,9 @@ class Agent: """ List the IDs of all steps that the task has created. """ - try: - steps, pagination = await self.db.list_steps(task_id, page, pageSize) - response = TaskStepsListResponse(steps=steps, pagination=pagination) - return response - except Exception as e: - raise + steps, pagination = await self.db.list_steps(task_id, page, pageSize) + response = TaskStepsListResponse(steps=steps, pagination=pagination) + return response async def execute_step(self, task_id: str, step_request: StepRequestBody) -> Step: """ @@ -148,11 +135,8 @@ class Agent: """ Get a step by ID. """ - try: - step = await self.db.get_step(task_id, step_id) - return step - except Exception as e: - raise + step = await self.db.get_step(task_id, step_id) + return step async def list_artifacts( self, task_id: str, page: int = 1, pageSize: int = 10 @@ -160,62 +144,45 @@ class Agent: """ List the artifacts that the task has created. """ - try: - artifacts, pagination = await self.db.list_artifacts( - task_id, page, pageSize - ) - return TaskArtifactsListResponse(artifacts=artifacts, pagination=pagination) - - except Exception as e: - raise + artifacts, pagination = await self.db.list_artifacts(task_id, page, pageSize) + return TaskArtifactsListResponse(artifacts=artifacts, pagination=pagination) async def create_artifact( - self, task_id: str, file: UploadFile, relative_path: str + self, task_id: str, file: UploadFile, relative_path: str = "" ) -> Artifact: """ Create an artifact for the task. """ - data = None file_name = file.filename or str(uuid4()) - try: - data = b"" - while contents := file.file.read(1024 * 1024): - data += contents - # Check if relative path ends with filename - if relative_path.endswith(file_name): - file_path = relative_path - else: - file_path = os.path.join(relative_path, file_name) + data = b"" + while contents := file.file.read(1024 * 1024): + data += contents + # Check if relative path ends with filename + if relative_path.endswith(file_name): + file_path = relative_path + else: + file_path = os.path.join(relative_path, file_name) - self.workspace.write(task_id, file_path, data) + await self.workspace.write_file(file_path, data) - artifact = await self.db.create_artifact( - task_id=task_id, - file_name=file_name, - relative_path=relative_path, - agent_created=False, - ) - except Exception as e: - raise + artifact = await self.db.create_artifact( + task_id=task_id, + file_name=file_name, + relative_path=relative_path, + agent_created=False, + ) return artifact - async def get_artifact(self, task_id: str, artifact_id: str) -> Artifact: + async def get_artifact(self, task_id: str, artifact_id: str) -> StreamingResponse: """ Get an artifact by ID. """ - try: - artifact = await self.db.get_artifact(artifact_id) - if artifact.file_name not in artifact.relative_path: - file_path = os.path.join(artifact.relative_path, artifact.file_name) - else: - file_path = artifact.relative_path - retrieved_artifact = self.workspace.read(task_id=task_id, path=file_path) - except NotFoundError as e: - raise - except FileNotFoundError as e: - raise - except Exception as e: - raise + artifact = await self.db.get_artifact(artifact_id) + if artifact.file_name not in artifact.relative_path: + file_path = os.path.join(artifact.relative_path, artifact.file_name) + else: + file_path = artifact.relative_path + retrieved_artifact = self.workspace.read_file(file_path, binary=True) return StreamingResponse( BytesIO(retrieved_artifact), diff --git a/forge/forge/agent/agent_test.py b/forge/forge/agent/agent_test.py new file mode 100644 index 0000000000..d8e4fcbe4d --- /dev/null +++ b/forge/forge/agent/agent_test.py @@ -0,0 +1,137 @@ +from pathlib import Path + +import pytest +from fastapi import UploadFile + +from forge.agent_protocol.database.db import AgentDB +from forge.agent_protocol.models.task import ( + StepRequestBody, + Task, + TaskListResponse, + TaskRequestBody, +) +from forge.file_storage.base import FileStorageConfiguration +from forge.file_storage.local import LocalFileStorage + +from .agent import Agent + + +@pytest.fixture +def agent(test_workspace: Path): + db = AgentDB("sqlite:///test.db") + config = FileStorageConfiguration(root=test_workspace) + workspace = LocalFileStorage(config) + return Agent(db, workspace) + + +@pytest.fixture +def file_upload(): + this_file = Path(__file__) + file_handle = this_file.open("rb") + yield UploadFile(file_handle, filename=this_file.name) + file_handle.close() + + +@pytest.mark.asyncio +async def test_create_task(agent: Agent): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task: Task = await agent.create_task(task_request) + assert task.input == "test_input" + + +@pytest.mark.asyncio +async def test_list_tasks(agent: Agent): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + await agent.create_task(task_request) + tasks = await agent.list_tasks() + assert isinstance(tasks, TaskListResponse) + + +@pytest.mark.asyncio +async def test_get_task(agent: Agent): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task = await agent.create_task(task_request) + retrieved_task = await agent.get_task(task.task_id) + assert retrieved_task.task_id == task.task_id + + +@pytest.mark.xfail(reason="execute_step is not implemented") +@pytest.mark.asyncio +async def test_execute_step(agent: Agent): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task = await agent.create_task(task_request) + step_request = StepRequestBody( + input="step_input", additional_input={"input": "additional_test_input"} + ) + step = await agent.execute_step(task.task_id, step_request) + assert step.input == "step_input" + assert step.additional_input == {"input": "additional_test_input"} + + +@pytest.mark.xfail(reason="execute_step is not implemented") +@pytest.mark.asyncio +async def test_get_step(agent: Agent): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task = await agent.create_task(task_request) + step_request = StepRequestBody( + input="step_input", additional_input={"input": "additional_test_input"} + ) + step = await agent.execute_step(task.task_id, step_request) + retrieved_step = await agent.get_step(task.task_id, step.step_id) + assert retrieved_step.step_id == step.step_id + + +@pytest.mark.asyncio +async def test_list_artifacts(agent: Agent): + tasks = await agent.list_tasks() + assert tasks.tasks, "No tasks in test.db" + + artifacts = await agent.list_artifacts(tasks.tasks[0].task_id) + assert isinstance(artifacts.artifacts, list) + + +@pytest.mark.asyncio +async def test_create_artifact(agent: Agent, file_upload: UploadFile): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task = await agent.create_task(task_request) + artifact = await agent.create_artifact( + task_id=task.task_id, + file=file_upload, + relative_path=f"a_dir/{file_upload.filename}", + ) + assert artifact.file_name == file_upload.filename + assert artifact.relative_path == f"a_dir/{file_upload.filename}" + + +@pytest.mark.asyncio +async def test_create_and_get_artifact(agent: Agent, file_upload: UploadFile): + task_request = TaskRequestBody( + input="test_input", additional_input={"input": "additional_test_input"} + ) + task = await agent.create_task(task_request) + + artifact = await agent.create_artifact( + task_id=task.task_id, + file=file_upload, + relative_path=f"b_dir/{file_upload.filename}", + ) + await file_upload.seek(0) + file_upload_content = await file_upload.read() + + retrieved_artifact = await agent.get_artifact(task.task_id, artifact.artifact_id) + retrieved_artifact_content = bytearray() + async for b in retrieved_artifact.body_iterator: + retrieved_artifact_content.extend(b) # type: ignore + assert retrieved_artifact_content == file_upload_content diff --git a/autogpts/forge/forge/agent/base.py b/forge/forge/agent/base.py similarity index 86% rename from autogpts/forge/forge/agent/base.py rename to forge/forge/agent/base.py index 8d37d5a8bf..0c3cc02d39 100644 --- a/autogpts/forge/forge/agent/base.py +++ b/forge/forge/agent/base.py @@ -5,22 +5,21 @@ import inspect import logging from abc import ABCMeta, abstractmethod from typing import ( - TYPE_CHECKING, Any, + Awaitable, Callable, + Generic, Iterator, Optional, ParamSpec, TypeVar, + cast, overload, ) from colorama import Fore from pydantic import BaseModel, Field, validator -if TYPE_CHECKING: - from forge.models.action import ActionProposal, ActionResult - from forge.agent import protocols from forge.agent.components import ( AgentComponent, @@ -29,27 +28,22 @@ from forge.agent.components import ( ) from forge.config.ai_directives import AIDirectives from forge.config.ai_profile import AIProfile -from forge.config.config import ConfigBuilder -from forge.llm.prompting.prompt import DEFAULT_TRIGGERING_PROMPT -from forge.llm.providers import ( - CHAT_MODELS, - AssistantFunctionCall, - ModelName, - OpenAIModelName, -) +from forge.llm.providers import CHAT_MODELS, ModelName, OpenAIModelName from forge.llm.providers.schema import ChatModelInfo -from forge.models.config import ( - Configurable, - SystemConfiguration, - SystemSettings, - UserConfigurable, -) +from forge.models.action import ActionResult, AnyProposal +from forge.models.config import SystemConfiguration, SystemSettings, UserConfigurable logger = logging.getLogger(__name__) T = TypeVar("T") P = ParamSpec("P") +DEFAULT_TRIGGERING_PROMPT = ( + "Determine exactly one command to use next based on the given goals " + "and the progress you have made so far, " + "and respond using the JSON schema specified previously:" +) + class BaseAgentConfiguration(SystemConfiguration): allow_fs_access: bool = UserConfigurable(default=False) @@ -114,11 +108,7 @@ class BaseAgentSettings(SystemSettings): ai_profile: AIProfile = Field(default_factory=lambda: AIProfile(ai_name="AutoGPT")) """The AI profile or "personality" of the agent.""" - directives: AIDirectives = Field( - default_factory=lambda: AIDirectives.from_file( - ConfigBuilder.default_settings.prompt_settings_file - ) - ) + directives: AIDirectives = Field(default_factory=AIDirectives) """Directives (general instructional guidelines) for the agent.""" task: str = "Terminate immediately" # FIXME: placeholder for forge.sdk.schema.Task @@ -137,17 +127,7 @@ class AgentMeta(ABCMeta): return instance - - - -class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): - C = TypeVar("C", bound=AgentComponent) - - default_settings = BaseAgentSettings( - name="BaseAgent", - description=__doc__ if __doc__ else "", - ) - +class BaseAgent(Generic[AnyProposal], metaclass=AgentMeta): def __init__( self, settings: BaseAgentSettings, @@ -177,13 +157,13 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): return self.config.send_token_limit or self.llm.max_tokens * 3 // 4 @abstractmethod - async def propose_action(self) -> ActionProposal: + async def propose_action(self) -> AnyProposal: ... @abstractmethod async def execute( self, - proposal: ActionProposal, + proposal: AnyProposal, user_feedback: str = "", ) -> ActionResult: ... @@ -191,7 +171,7 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): @abstractmethod async def do_not_execute( self, - denied_proposal: ActionProposal, + denied_proposal: AnyProposal, user_feedback: str, ) -> ActionResult: ... @@ -207,13 +187,16 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): @overload async def run_pipeline( - self, protocol_method: Callable[P, None], *args, retry_limit: int = 3 + self, + protocol_method: Callable[P, None | Awaitable[None]], + *args, + retry_limit: int = 3, ) -> list[None]: ... async def run_pipeline( self, - protocol_method: Callable[P, Iterator[T] | None], + protocol_method: Callable[P, Iterator[T] | None | Awaitable[None]], *args, retry_limit: int = 3, ) -> list[T] | list[None]: @@ -244,7 +227,10 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): ) continue - method = getattr(component, method_name, None) + method = cast( + Callable[..., Iterator[T] | None | Awaitable[None]] | None, + getattr(component, method_name, None), + ) if not callable(method): continue @@ -252,10 +238,9 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): while component_attempts < retry_limit: try: component_args = self._selective_copy(args) - if inspect.iscoroutinefunction(method): - result = await method(*component_args) - else: - result = method(*component_args) + result = method(*component_args) + if inspect.isawaitable(result): + result = await result if result is not None: method_result.extend(result) args = component_args @@ -273,9 +258,9 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): break # Successful pipeline execution break - except EndpointPipelineError: + except EndpointPipelineError as e: self._trace.append( - f"❌ {Fore.LIGHTRED_EX}{component.__class__.__name__}: " + f"❌ {Fore.LIGHTRED_EX}{e.triggerer.__class__.__name__}: " f"EndpointPipelineError{Fore.RESET}" ) # Restart from the beginning on EndpointPipelineError @@ -302,7 +287,7 @@ class BaseAgent(Configurable[BaseAgentSettings], metaclass=AgentMeta): f"Component {component.__class__.__name__} " "is attached to an agent but not added to components list" ) - # Skip collecting anf sorting and sort if ordering is explicit + # Skip collecting and sorting and sort if ordering is explicit return self.components = self._topological_sort(components) diff --git a/autogpts/forge/forge/agent/components.py b/forge/forge/agent/components.py similarity index 87% rename from autogpts/forge/forge/agent/components.py rename to forge/forge/agent/components.py index 88854dca8c..65db2fb605 100644 --- a/autogpts/forge/forge/agent/components.py +++ b/forge/forge/agent/components.py @@ -36,15 +36,16 @@ class AgentComponent(ABC): class ComponentEndpointError(Exception): """Error of a single protocol method on a component.""" - def __init__(self, message: str = ""): + def __init__(self, message: str, component: AgentComponent): self.message = message + self.triggerer = component super().__init__(message) class EndpointPipelineError(ComponentEndpointError): - """Error of an entire pipline of one endpoint.""" + """Error of an entire pipeline of one endpoint.""" class ComponentSystemError(EndpointPipelineError): """Error of a group of pipelines; - multiple different enpoints.""" + multiple different endpoints.""" diff --git a/autogpts/forge/forge/agent/protocols.py b/forge/forge/agent/protocols.py similarity index 68% rename from autogpts/forge/forge/agent/protocols.py rename to forge/forge/agent/protocols.py index a1d9fb27c2..5035778236 100644 --- a/autogpts/forge/forge/agent/protocols.py +++ b/forge/forge/agent/protocols.py @@ -1,14 +1,13 @@ from abc import abstractmethod -from typing import TYPE_CHECKING, Iterator +from typing import TYPE_CHECKING, Awaitable, Generic, Iterator + +from forge.models.action import ActionResult, AnyProposal from .components import AgentComponent if TYPE_CHECKING: from forge.command.command import Command from forge.llm.providers import ChatMessage - from forge.models.action import ActionResult - - from .base import ActionProposal class DirectiveProvider(AgentComponent): @@ -34,19 +33,19 @@ class MessageProvider(AgentComponent): ... -class AfterParse(AgentComponent): +class AfterParse(AgentComponent, Generic[AnyProposal]): @abstractmethod - def after_parse(self, result: "ActionProposal") -> None: + def after_parse(self, result: AnyProposal) -> None | Awaitable[None]: ... class ExecutionFailure(AgentComponent): @abstractmethod - def execution_failure(self, error: Exception) -> None: + def execution_failure(self, error: Exception) -> None | Awaitable[None]: ... class AfterExecute(AgentComponent): @abstractmethod - def after_execute(self, result: "ActionResult") -> None: + def after_execute(self, result: "ActionResult") -> None | Awaitable[None]: ... diff --git a/autogpts/autogpt/tests/__init__.py b/forge/forge/agent_protocol/__init__.py similarity index 100% rename from autogpts/autogpt/tests/__init__.py rename to forge/forge/agent_protocol/__init__.py diff --git a/autogpts/forge/forge/sdk/routes/agent_protocol.py b/forge/forge/agent_protocol/api_router.py similarity index 51% rename from autogpts/forge/forge/sdk/routes/agent_protocol.py rename to forge/forge/agent_protocol/api_router.py index 1ea739ca0f..e064e19490 100644 --- a/autogpts/forge/forge/sdk/routes/agent_protocol.py +++ b/forge/forge/agent_protocol/api_router.py @@ -1,35 +1,18 @@ """ Routes for the Agent Service. -This module defines the API routes for the Agent service. While there are multiple endpoints provided by the service, -the ones that require special attention due to their complexity are: +This module defines the API routes for the Agent service. -1. `execute_agent_task_step`: - This route is significant because this is where the agent actually performs the work. The function handles - executing the next step for a task based on its current state, and it requires careful implementation to ensure - all scenarios (like the presence or absence of steps or a step marked as `last_step`) are handled correctly. - -2. `upload_agent_task_artifacts`: - This route allows for the upload of artifacts, supporting various URI types (e.g., s3, gcs, ftp, http). - The support for different URI types makes it a bit more complex, and it's important to ensure that all - supported URI types are correctly managed. NOTE: The AutoGPT team will eventually handle the most common - uri types for you. - -3. `create_agent_task`: - While this is a simpler route, it plays a crucial role in the workflow, as it's responsible for the creation - of a new task. - -Developers and contributors should be especially careful when making modifications to these routes to ensure -consistency and correctness in the system's behavior. +Developers and contributors should be especially careful when making modifications +to these routes to ensure consistency and correctness in the system's behavior. """ -import json -from typing import Optional +import logging +from typing import TYPE_CHECKING, Optional -from fastapi import APIRouter, Query, Request, Response, UploadFile -from fastapi.responses import FileResponse +from fastapi import APIRouter, HTTPException, Query, Request, Response, UploadFile +from fastapi.responses import StreamingResponse -from forge.sdk.forge_log import ForgeLogger -from forge.sdk.model import ( +from .models import ( Artifact, Step, StepRequestBody, @@ -39,15 +22,12 @@ from forge.sdk.model import ( TaskRequestBody, TaskStepsListResponse, ) -from forge.utils.exceptions import ( - NotFoundError, - get_detailed_traceback, - get_exception_message, -) + +if TYPE_CHECKING: + from forge.agent.agent import Agent base_router = APIRouter() - -LOG = ForgeLogger(__name__) +logger = logging.getLogger(__name__) @base_router.get("/", tags=["root"]) @@ -73,10 +53,10 @@ async def create_agent_task(request: Request, task_request: TaskRequestBody) -> Args: request (Request): FastAPI request object. - task (TaskRequestBody): The task request containing input and additional input data. + task (TaskRequestBody): The task request containing input data. Returns: - Task: A new task with task_id, input, additional_input, and empty lists for artifacts and steps. + Task: A new task with task_id, input, and additional_input set. Example: Request (TaskRequestBody defined in schema.py): @@ -93,46 +73,32 @@ async def create_agent_task(request: Request, task_request: TaskRequestBody) -> "artifacts": [], } """ - agent = request["agent"] + agent: "Agent" = request["agent"] try: - task_request = await agent.create_task(task_request) - return Response( - content=task_request.json(), - status_code=200, - media_type="application/json", - ) + task = await agent.create_task(task_request) + return task except Exception: - LOG.exception(f"Error whilst trying to create a task: {task_request}") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to create a task: {task_request}") + raise @base_router.get("/agent/tasks", tags=["agent"], response_model=TaskListResponse) async def list_agent_tasks( request: Request, - page: Optional[int] = Query(1, ge=1), - page_size: Optional[int] = Query(10, ge=1), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1), ) -> TaskListResponse: """ Retrieves a paginated list of all tasks. Args: request (Request): FastAPI request object. - page (int, optional): The page number for pagination. Defaults to 1. - page_size (int, optional): The number of tasks per page for pagination. Defaults to 10. + page (int, optional): Page number for pagination. Default: 1 + page_size (int, optional): Number of tasks per page for pagination. Default: 10 Returns: - TaskListResponse: A response object containing a list of tasks and pagination details. + TaskListResponse: A list of tasks, and pagination details. Example: Request: @@ -158,34 +124,13 @@ async def list_agent_tasks( } } """ - agent = request["agent"] + agent: "Agent" = request["agent"] try: tasks = await agent.list_tasks(page, page_size) - return Response( - content=tasks.json(), - status_code=200, - media_type="application/json", - ) - except NotFoundError: - LOG.exception("Error whilst trying to list tasks") - return Response( - content=json.dumps({"error": "Tasks not found"}), - status_code=404, - media_type="application/json", - ) + return tasks except Exception: - LOG.exception("Error whilst trying to list tasks") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception("Error whilst trying to list tasks") + raise @base_router.get("/agent/tasks/{task_id}", tags=["agent"], response_model=Task) @@ -239,36 +184,14 @@ async def get_agent_task(request: Request, task_id: str) -> Task: } ] } - """ - agent = request["agent"] + """ # noqa: E501 + agent: "Agent" = request["agent"] try: task = await agent.get_task(task_id) - - return Response( - content=task.json(), - status_code=200, - media_type="application/json", - ) - except NotFoundError: - LOG.exception(f"Error whilst trying to get task: {task_id}") - return Response( - content=json.dumps({"error": "Task not found"}), - status_code=404, - media_type="application/json", - ) + return task except Exception: - LOG.exception(f"Error whilst trying to get task: {task_id}") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to get task: {task_id}") + raise @base_router.get( @@ -279,8 +202,8 @@ async def get_agent_task(request: Request, task_id: str) -> Task: async def list_agent_task_steps( request: Request, task_id: str, - page: Optional[int] = Query(1, ge=1), - page_size: Optional[int] = Query(10, ge=1, alias="pageSize"), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1, alias="pageSize"), ) -> TaskStepsListResponse: """ Retrieves a paginated list of steps associated with a specific task. @@ -289,10 +212,10 @@ async def list_agent_task_steps( request (Request): FastAPI request object. task_id (str): The ID of the task. page (int, optional): The page number for pagination. Defaults to 1. - page_size (int, optional): The number of steps per page for pagination. Defaults to 10. + page_size (int, optional): Number of steps per page for pagination. Default: 10. Returns: - TaskStepsListResponse: A response object containing a list of steps and pagination details. + TaskStepsListResponse: A list of steps, and pagination details. Example: Request: @@ -315,54 +238,40 @@ async def list_agent_task_steps( "pageSize": 10 } } - """ - agent = request["agent"] + """ # noqa: E501 + agent: "Agent" = request["agent"] try: steps = await agent.list_steps(task_id, page, page_size) - return Response( - content=steps.json(), - status_code=200, - media_type="application/json", - ) - except NotFoundError: - LOG.exception("Error whilst trying to list steps") - return Response( - content=json.dumps({"error": "Steps not found"}), - status_code=404, - media_type="application/json", - ) + return steps except Exception: - LOG.exception("Error whilst trying to list steps") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception("Error whilst trying to list steps") + raise @base_router.post("/agent/tasks/{task_id}/steps", tags=["agent"], response_model=Step) async def execute_agent_task_step( - request: Request, task_id: str, step: Optional[StepRequestBody] = None + request: Request, task_id: str, step_request: Optional[StepRequestBody] = None ) -> Step: """ - Executes the next step for a specified task based on the current task status and returns the - executed step with additional feedback fields. + Executes the next step for a specified task based on the current task status and + returns the executed step with additional feedback fields. - Depending on the current state of the task, the following scenarios are supported: + This route is significant because this is where the agent actually performs work. + The function handles executing the next step for a task based on its current state, + and it requires careful implementation to ensure all scenarios (like the presence + or absence of steps or a step marked as `last_step`) are handled correctly. + Depending on the current state of the task, the following scenarios are possible: 1. No steps exist for the task. - 2. There is at least one step already for the task, and the task does not have a completed step marked as `last_step`. + 2. There is at least one step already for the task, and the task does not have a + completed step marked as `last_step`. 3. There is a completed step marked as `last_step` already on the task. - In each of these scenarios, a step object will be returned with two additional fields: `output` and `additional_output`. + In each of these scenarios, a step object will be returned with two additional + fields: `output` and `additional_output`. - `output`: Provides the primary response or feedback to the user. - - `additional_output`: Supplementary information or data. Its specific content is not strictly defined and can vary based on the step or agent's implementation. + - `additional_output`: Supplementary information or data. Its specific content is + not strictly defined and can vary based on the step or agent's implementation. Args: request (Request): FastAPI request object. @@ -389,39 +298,17 @@ async def execute_agent_task_step( ... } """ - agent = request["agent"] + agent: "Agent" = request["agent"] try: # An empty step request represents a yes to continue command - if not step: - step = StepRequestBody(input="y") + if not step_request: + step_request = StepRequestBody(input="y") - step = await agent.execute_step(task_id, step) - - return Response( - content=step.json(), - status_code=200, - media_type="application/json", - ) - except NotFoundError: - LOG.exception(f"Error whilst trying to execute a task step: {task_id}") - return Response( - content=json.dumps({"error": f"Task not found {task_id}"}), - status_code=404, - media_type="application/json", - ) + step = await agent.execute_step(task_id, step_request) + return step except Exception: - LOG.exception(f"Error whilst trying to execute a task step: {task_id}") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to execute a task step: {task_id}") + raise @base_router.get( @@ -450,31 +337,13 @@ async def get_agent_task_step(request: Request, task_id: str, step_id: str) -> S ... } """ - agent = request["agent"] + agent: "Agent" = request["agent"] try: step = await agent.get_step(task_id, step_id) - - return Response(content=step.json(), status_code=200) - except NotFoundError: - LOG.exception(f"Error whilst trying to get step: {step_id}") - return Response( - content=json.dumps({"error": "Step not found"}), - status_code=404, - media_type="application/json", - ) + return step except Exception: - LOG.exception(f"Error whilst trying to get step: {step_id}") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to get step: {step_id}") + raise @base_router.get( @@ -485,8 +354,8 @@ async def get_agent_task_step(request: Request, task_id: str, step_id: str) -> S async def list_agent_task_artifacts( request: Request, task_id: str, - page: Optional[int] = Query(1, ge=1), - page_size: Optional[int] = Query(10, ge=1, alias="pageSize"), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1, alias="pageSize"), ) -> TaskArtifactsListResponse: """ Retrieves a paginated list of artifacts associated with a specific task. @@ -495,10 +364,10 @@ async def list_agent_task_artifacts( request (Request): FastAPI request object. task_id (str): The ID of the task. page (int, optional): The page number for pagination. Defaults to 1. - page_size (int, optional): The number of items per page for pagination. Defaults to 10. + page_size (int, optional): Number of items per page for pagination. Default: 10. Returns: - TaskArtifactsListResponse: A response object containing a list of artifacts and pagination details. + TaskArtifactsListResponse: A list of artifacts, and pagination details. Example: Request: @@ -518,52 +387,33 @@ async def list_agent_task_artifacts( "pageSize": 10 } } - """ - agent = request["agent"] + """ # noqa: E501 + agent: "Agent" = request["agent"] try: - artifacts: TaskArtifactsListResponse = await agent.list_artifacts( - task_id, page, page_size - ) + artifacts = await agent.list_artifacts(task_id, page, page_size) return artifacts - except NotFoundError: - LOG.exception("Error whilst trying to list artifacts") - return Response( - content=json.dumps({"error": "Artifacts not found for task_id"}), - status_code=404, - media_type="application/json", - ) except Exception: - LOG.exception("Error whilst trying to list artifacts") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception("Error whilst trying to list artifacts") + raise @base_router.post( "/agent/tasks/{task_id}/artifacts", tags=["agent"], response_model=Artifact ) async def upload_agent_task_artifacts( - request: Request, task_id: str, file: UploadFile, relative_path: Optional[str] = "" + request: Request, task_id: str, file: UploadFile, relative_path: str = "" ) -> Artifact: """ - This endpoint is used to upload an artifact associated with a specific task. The artifact is provided as a file. + This endpoint is used to upload an artifact (file) associated with a specific task. Args: request (Request): The FastAPI request object. - task_id (str): The unique identifier of the task for which the artifact is being uploaded. + task_id (str): The ID of the task for which the artifact is being uploaded. file (UploadFile): The file being uploaded as an artifact. relative_path (str): The relative path for the file. This is a query parameter. Returns: - Artifact: An object containing metadata of the uploaded artifact, including its unique identifier. + Artifact: Metadata object for the uploaded artifact, including its ID and path. Example: Request: @@ -579,35 +429,17 @@ async def upload_agent_task_artifacts( "relative_path": "/my_folder/my_other_folder/", "file_name": "main.py" } - """ - agent = request["agent"] + """ # noqa: E501 + agent: "Agent" = request["agent"] if file is None: - return Response( - content=json.dumps({"error": "File must be specified"}), - status_code=404, - media_type="application/json", - ) + raise HTTPException(status_code=400, detail="File must be specified") try: artifact = await agent.create_artifact(task_id, file, relative_path) - return Response( - content=artifact.json(), - status_code=200, - media_type="application/json", - ) + return artifact except Exception: - LOG.exception(f"Error whilst trying to upload artifact: {task_id}") - return Response( - content=json.dumps( - { - "error": "Internal server error", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to upload artifact: {task_id}") + raise @base_router.get( @@ -617,7 +449,7 @@ async def upload_agent_task_artifacts( ) async def download_agent_task_artifact( request: Request, task_id: str, artifact_id: str -) -> FileResponse: +) -> StreamingResponse: """ Downloads an artifact associated with a specific task. @@ -636,32 +468,9 @@ async def download_agent_task_artifact( Response: """ - agent = request["agent"] + agent: "Agent" = request["agent"] try: return await agent.get_artifact(task_id, artifact_id) - except NotFoundError: - LOG.exception(f"Error whilst trying to download artifact: {task_id}") - return Response( - content=json.dumps( - { - "error": f"Artifact not found " - "- task_id: {task_id}, artifact_id: {artifact_id}" - } - ), - status_code=404, - media_type="application/json", - ) except Exception: - LOG.exception(f"Error whilst trying to download artifact: {task_id}") - return Response( - content=json.dumps( - { - "error": f"Internal server error " - "- task_id: {task_id}, artifact_id: {artifact_id}", - "exception": get_exception_message(), - "traceback": get_detailed_traceback(), - } - ), - status_code=500, - media_type="application/json", - ) + logger.exception(f"Error whilst trying to download artifact: {task_id}") + raise diff --git a/forge/forge/agent_protocol/database/__init__.py b/forge/forge/agent_protocol/database/__init__.py new file mode 100644 index 0000000000..c0a1a34fe0 --- /dev/null +++ b/forge/forge/agent_protocol/database/__init__.py @@ -0,0 +1,3 @@ +from .db import AgentDB + +__all__ = ["AgentDB"] diff --git a/autogpts/forge/forge/sdk/db.py b/forge/forge/agent_protocol/database/db.py similarity index 70% rename from autogpts/forge/forge/sdk/db.py rename to forge/forge/agent_protocol/database/db.py index 127ccdcecf..e62702bb80 100644 --- a/autogpts/forge/forge/sdk/db.py +++ b/forge/forge/agent_protocol/database/db.py @@ -4,44 +4,47 @@ It uses SQLite as the database and file store backend. IT IS NOT ADVISED TO USE THIS IN PRODUCTION! """ -import datetime +import logging import math import uuid +from datetime import datetime from typing import Any, Dict, List, Literal, Optional, Tuple -from sqlalchemy import ( - JSON, - Boolean, - Column, - DateTime, - ForeignKey, - String, - create_engine, -) +from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, create_engine from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import DeclarativeBase, joinedload, relationship, sessionmaker +from sqlalchemy.orm import ( + DeclarativeBase, + Mapped, + joinedload, + mapped_column, + relationship, + sessionmaker, +) from forge.utils.exceptions import NotFoundError -from .forge_log import ForgeLogger -from .model import Artifact, Pagination, Status, Step, StepRequestBody, Task +from ..models.artifact import Artifact +from ..models.pagination import Pagination +from ..models.task import Step, StepRequestBody, StepStatus, Task -LOG = ForgeLogger(__name__) +logger = logging.getLogger(__name__) class Base(DeclarativeBase): - pass + type_annotation_map = { + dict[str, Any]: JSON, + } class TaskModel(Base): __tablename__ = "tasks" - task_id = Column(String, primary_key=True, index=True) - input = Column(String) - additional_input = Column(JSON) - created_at = Column(DateTime, default=datetime.datetime.utcnow) - modified_at = Column( - DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow + task_id: Mapped[str] = mapped_column(primary_key=True, index=True) + input: Mapped[str] + additional_input: Mapped[dict[str, Any]] = mapped_column(default=dict) + created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) + modified_at: Mapped[datetime] = mapped_column( + DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) artifacts = relationship("ArtifactModel", back_populates="task") @@ -50,35 +53,35 @@ class TaskModel(Base): class StepModel(Base): __tablename__ = "steps" - step_id = Column(String, primary_key=True, index=True) - task_id = Column(String, ForeignKey("tasks.task_id")) - name = Column(String) - input = Column(String) - status = Column(String) - output = Column(String) - is_last = Column(Boolean, default=False) - created_at = Column(DateTime, default=datetime.datetime.utcnow) - modified_at = Column( - DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow + step_id: Mapped[str] = mapped_column(primary_key=True, index=True) + task_id: Mapped[str] = mapped_column(ForeignKey("tasks.task_id")) + name: Mapped[str] + input: Mapped[str] + status: Mapped[str] + output: Mapped[Optional[str]] + is_last: Mapped[bool] = mapped_column(Boolean, default=False) + created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) + modified_at: Mapped[datetime] = mapped_column( + DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) - additional_input = Column(JSON) - additional_output = Column(JSON) + additional_input: Mapped[dict[str, Any]] = mapped_column(default=dict) + additional_output: Mapped[Optional[dict[str, Any]]] artifacts = relationship("ArtifactModel", back_populates="step") class ArtifactModel(Base): __tablename__ = "artifacts" - artifact_id = Column(String, primary_key=True, index=True) - task_id = Column(String, ForeignKey("tasks.task_id")) - step_id = Column(String, ForeignKey("steps.step_id")) - agent_created = Column(Boolean, default=False) - file_name = Column(String) - relative_path = Column(String) - created_at = Column(DateTime, default=datetime.datetime.utcnow) - modified_at = Column( - DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow + artifact_id: Mapped[str] = mapped_column(primary_key=True, index=True) + task_id: Mapped[str] = mapped_column(ForeignKey("tasks.task_id")) + step_id: Mapped[Optional[str]] = mapped_column(ForeignKey("steps.step_id")) + agent_created: Mapped[bool] = mapped_column(default=False) + file_name: Mapped[str] + relative_path: Mapped[str] + created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) + modified_at: Mapped[datetime] = mapped_column( + default=datetime.utcnow, onupdate=datetime.utcnow ) step = relationship("StepModel", back_populates="artifacts") @@ -87,7 +90,7 @@ class ArtifactModel(Base): def convert_to_task(task_obj: TaskModel, debug_enabled: bool = False) -> Task: if debug_enabled: - LOG.debug(f"Converting TaskModel to Task for task_id: {task_obj.task_id}") + logger.debug(f"Converting TaskModel to Task for task_id: {task_obj.task_id}") task_artifacts = [convert_to_artifact(artifact) for artifact in task_obj.artifacts] return Task( task_id=task_obj.task_id, @@ -101,11 +104,13 @@ def convert_to_task(task_obj: TaskModel, debug_enabled: bool = False) -> Task: def convert_to_step(step_model: StepModel, debug_enabled: bool = False) -> Step: if debug_enabled: - LOG.debug(f"Converting StepModel to Step for step_id: {step_model.step_id}") + logger.debug(f"Converting StepModel to Step for step_id: {step_model.step_id}") step_artifacts = [ convert_to_artifact(artifact) for artifact in step_model.artifacts ] - status = Status.completed if step_model.status == "completed" else Status.created + status = ( + StepStatus.completed if step_model.status == "completed" else StepStatus.created + ) return Step( task_id=step_model.task_id, step_id=step_model.step_id, @@ -139,16 +144,22 @@ class AgentDB: super().__init__() self.debug_enabled = debug_enabled if self.debug_enabled: - LOG.debug(f"Initializing AgentDB with database_string: {database_string}") + logger.debug( + f"Initializing AgentDB with database_string: {database_string}" + ) self.engine = create_engine(database_string) Base.metadata.create_all(self.engine) self.Session = sessionmaker(bind=self.engine) + def close(self) -> None: + self.Session.close_all() + self.engine.dispose() + async def create_task( self, input: Optional[str], additional_input: Optional[dict] = {} ) -> Task: if self.debug_enabled: - LOG.debug("Creating new task") + logger.debug("Creating new task") try: with self.Session() as session: @@ -161,15 +172,13 @@ class AgentDB: session.commit() session.refresh(new_task) if self.debug_enabled: - LOG.debug(f"Created new task with task_id: {new_task.task_id}") + logger.debug(f"Created new task with task_id: {new_task.task_id}") return convert_to_task(new_task, self.debug_enabled) except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while creating task: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while creating task: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while creating task: {e}") + logger.error(f"Unexpected error while creating task: {e}") raise async def create_step( @@ -180,7 +189,7 @@ class AgentDB: additional_input: Optional[Dict[str, Any]] = {}, ) -> Step: if self.debug_enabled: - LOG.debug(f"Creating new step for task_id: {task_id}") + logger.debug(f"Creating new step for task_id: {task_id}") try: with self.Session() as session: new_step = StepModel( @@ -196,15 +205,13 @@ class AgentDB: session.commit() session.refresh(new_step) if self.debug_enabled: - LOG.debug(f"Created new step with step_id: {new_step.step_id}") + logger.debug(f"Created new step with step_id: {new_step.step_id}") return convert_to_step(new_step, self.debug_enabled) except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while creating step: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while creating step: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while creating step: {e}") + logger.error(f"Unexpected error while creating step: {e}") raise async def create_artifact( @@ -216,7 +223,7 @@ class AgentDB: step_id: str | None = None, ) -> Artifact: if self.debug_enabled: - LOG.debug(f"Creating new artifact for task_id: {task_id}") + logger.debug(f"Creating new artifact for task_id: {task_id}") try: with self.Session() as session: if ( @@ -230,8 +237,8 @@ class AgentDB: ): session.close() if self.debug_enabled: - LOG.debug( - f"Artifact already exists with relative_path: {relative_path}" + logger.debug( + f"Artifact {file_name} already exists at {relative_path}/" ) return convert_to_artifact(existing_artifact) @@ -247,23 +254,21 @@ class AgentDB: session.commit() session.refresh(new_artifact) if self.debug_enabled: - LOG.debug( - f"Created new artifact with artifact_id: {new_artifact.artifact_id}" + logger.debug( + f"Created new artifact with ID: {new_artifact.artifact_id}" ) return convert_to_artifact(new_artifact) except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while creating step: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while creating step: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while creating step: {e}") + logger.error(f"Unexpected error while creating step: {e}") raise async def get_task(self, task_id: str) -> Task: """Get a task by its id""" if self.debug_enabled: - LOG.debug(f"Getting task with task_id: {task_id}") + logger.debug(f"Getting task with task_id: {task_id}") try: with self.Session() as session: if task_obj := ( @@ -274,20 +279,18 @@ class AgentDB: ): return convert_to_task(task_obj, self.debug_enabled) else: - LOG.error(f"Task not found with task_id: {task_id}") + logger.error(f"Task not found with task_id: {task_id}") raise NotFoundError("Task not found") except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting task: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while getting task: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while getting task: {e}") + logger.error(f"Unexpected error while getting task: {e}") raise async def get_step(self, task_id: str, step_id: str) -> Step: if self.debug_enabled: - LOG.debug(f"Getting step with task_id: {task_id} and step_id: {step_id}") + logger.debug(f"Getting step with task_id: {task_id} and step_id: {step_id}") try: with self.Session() as session: if step := ( @@ -299,22 +302,20 @@ class AgentDB: return convert_to_step(step, self.debug_enabled) else: - LOG.error( + logger.error( f"Step not found with task_id: {task_id} and step_id: {step_id}" ) raise NotFoundError("Step not found") except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting step: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while getting step: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while getting step: {e}") + logger.error(f"Unexpected error while getting step: {e}") raise async def get_artifact(self, artifact_id: str) -> Artifact: if self.debug_enabled: - LOG.debug(f"Getting artifact with and artifact_id: {artifact_id}") + logger.debug(f"Getting artifact with and artifact_id: {artifact_id}") try: with self.Session() as session: if ( @@ -324,15 +325,15 @@ class AgentDB: ): return convert_to_artifact(artifact_model) else: - LOG.error(f"Artifact not found with and artifact_id: {artifact_id}") + logger.error( + f"Artifact not found with and artifact_id: {artifact_id}" + ) raise NotFoundError("Artifact not found") except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting artifact: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while getting artifact: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while getting artifact: {e}") + logger.error(f"Unexpected error while getting artifact: {e}") raise async def update_step( @@ -345,7 +346,9 @@ class AgentDB: additional_output: Optional[Dict[str, Any]] = None, ) -> Step: if self.debug_enabled: - LOG.debug(f"Updating step with task_id: {task_id} and step_id: {step_id}") + logger.debug( + f"Updating step with task_id: {task_id} and step_id: {step_id}" + ) try: with self.Session() as session: if ( @@ -364,17 +367,16 @@ class AgentDB: session.commit() return await self.get_step(task_id, step_id) else: - LOG.error( - f"Step not found for update with task_id: {task_id} and step_id: {step_id}" + logger.error( + "Can't update non-existent Step with " + f"task_id: {task_id} and step_id: {step_id}" ) raise NotFoundError("Step not found") except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while getting step: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while getting step: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while getting step: {e}") + logger.error(f"Unexpected error while getting step: {e}") raise async def update_artifact( @@ -385,7 +387,7 @@ class AgentDB: relative_path: str = "", agent_created: Optional[Literal[True]] = None, ) -> Artifact: - LOG.debug(f"Updating artifact with artifact_id: {artifact_id}") + logger.debug(f"Updating artifact with artifact_id: {artifact_id}") with self.Session() as session: if ( artifact := session.query(ArtifactModel) @@ -401,14 +403,14 @@ class AgentDB: session.commit() return await self.get_artifact(artifact_id) else: - LOG.error(f"Artifact not found with artifact_id: {artifact_id}") + logger.error(f"Artifact not found with artifact_id: {artifact_id}") raise NotFoundError("Artifact not found") async def list_tasks( self, page: int = 1, per_page: int = 10 ) -> Tuple[List[Task], Pagination]: if self.debug_enabled: - LOG.debug("Listing tasks") + logger.debug("Listing tasks") try: with self.Session() as session: tasks = ( @@ -429,19 +431,17 @@ class AgentDB: convert_to_task(task, self.debug_enabled) for task in tasks ], pagination except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while listing tasks: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while listing tasks: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while listing tasks: {e}") + logger.error(f"Unexpected error while listing tasks: {e}") raise async def list_steps( self, task_id: str, page: int = 1, per_page: int = 10 ) -> Tuple[List[Step], Pagination]: if self.debug_enabled: - LOG.debug(f"Listing steps for task_id: {task_id}") + logger.debug(f"Listing steps for task_id: {task_id}") try: with self.Session() as session: steps = ( @@ -463,19 +463,17 @@ class AgentDB: convert_to_step(step, self.debug_enabled) for step in steps ], pagination except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while listing steps: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while listing steps: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while listing steps: {e}") + logger.error(f"Unexpected error while listing steps: {e}") raise async def list_artifacts( self, task_id: str, page: int = 1, per_page: int = 10 ) -> Tuple[List[Artifact], Pagination]: if self.debug_enabled: - LOG.debug(f"Listing artifacts for task_id: {task_id}") + logger.debug(f"Listing artifacts for task_id: {task_id}") try: with self.Session() as session: artifacts = ( @@ -497,10 +495,8 @@ class AgentDB: convert_to_artifact(artifact) for artifact in artifacts ], pagination except SQLAlchemyError as e: - LOG.error(f"SQLAlchemy error while listing artifacts: {e}") - raise - except NotFoundError as e: + logger.error(f"SQLAlchemy error while listing artifacts: {e}") raise except Exception as e: - LOG.error(f"Unexpected error while listing artifacts: {e}") + logger.error(f"Unexpected error while listing artifacts: {e}") raise diff --git a/autogpts/forge/forge/sdk/db_test.py b/forge/forge/agent_protocol/database/db_test.py similarity index 70% rename from autogpts/forge/forge/sdk/db_test.py rename to forge/forge/agent_protocol/database/db_test.py index 05f4b87547..235b21143f 100644 --- a/autogpts/forge/forge/sdk/db_test.py +++ b/forge/forge/agent_protocol/database/db_test.py @@ -4,7 +4,7 @@ from datetime import datetime import pytest -from forge.sdk.db import ( +from forge.agent_protocol.database.db import ( AgentDB, ArtifactModel, StepModel, @@ -13,17 +13,36 @@ from forge.sdk.db import ( convert_to_step, convert_to_task, ) -from forge.sdk.model import Artifact, Status, Step, StepRequestBody, Task +from forge.agent_protocol.models import ( + Artifact, + Step, + StepRequestBody, + StepStatus, + Task, +) from forge.utils.exceptions import NotFoundError as DataNotFoundError +TEST_DB_FILENAME = "test_db.sqlite3" +TEST_DB_URL = f"sqlite:///{TEST_DB_FILENAME}" -@pytest.mark.asyncio -def test_table_creation(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) - conn = sqlite3.connect("test_db.sqlite3") - cursor = conn.cursor() +@pytest.fixture +def agent_db(): + db = AgentDB(TEST_DB_URL) + yield db + db.close() + os.remove(TEST_DB_FILENAME) + + +@pytest.fixture +def raw_db_connection(agent_db: AgentDB): + connection = sqlite3.connect(TEST_DB_FILENAME) + yield connection + connection.close() + + +def test_table_creation(raw_db_connection: sqlite3.Connection): + cursor = raw_db_connection.cursor() # Test for tasks table existence cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='tasks'") @@ -39,8 +58,6 @@ def test_table_creation(): ) assert cursor.fetchone() is not None - os.remove(db_name.split("///")[1]) - @pytest.mark.asyncio async def test_task_schema(): @@ -77,8 +94,11 @@ async def test_step_schema(): modified_at=now, name="Write to file", input="Write the words you receive to the file 'output.txt'.", - status=Status.created, - output="I am going to use the write_to_file command and write Washington to a file called output.txt ", + status=StepStatus.created, + output=( + "I am going to use the write_to_file command and write Washington " + "to a file called output.txt " + ), artifacts=[ Artifact( artifact_id="b225e278-8b4c-4f99-a696-8facf19f0e56", @@ -94,14 +114,14 @@ async def test_step_schema(): assert step.task_id == "50da533e-3904-4401-8a07-c49adf88b5eb" assert step.step_id == "6bb1801a-fd80-45e8-899a-4dd723cc602e" assert step.name == "Write to file" - assert step.status == Status.created - assert ( - step.output - == "I am going to use the write_to_file command and write Washington to a file called output.txt " + assert step.status == StepStatus.created + assert step.output == ( + "I am going to use the write_to_file command and write Washington " + "to a file called output.txt " ) assert len(step.artifacts) == 1 assert step.artifacts[0].artifact_id == "b225e278-8b4c-4f99-a696-8facf19f0e56" - assert step.is_last == False + assert step.is_last is False @pytest.mark.asyncio @@ -112,6 +132,7 @@ async def test_convert_to_task(): created_at=now, modified_at=now, input="Write the words you receive to the file 'output.txt'.", + additional_input={}, artifacts=[ ArtifactModel( artifact_id="b225e278-8b4c-4f99-a696-8facf19f0e56", @@ -141,6 +162,7 @@ async def test_convert_to_step(): name="Write to file", status="created", input="Write the words you receive to the file 'output.txt'.", + additional_input={}, artifacts=[ ArtifactModel( artifact_id="b225e278-8b4c-4f99-a696-8facf19f0e56", @@ -157,10 +179,10 @@ async def test_convert_to_step(): assert step.task_id == "50da533e-3904-4401-8a07-c49adf88b5eb" assert step.step_id == "6bb1801a-fd80-45e8-899a-4dd723cc602e" assert step.name == "Write to file" - assert step.status == Status.created + assert step.status == StepStatus.created assert len(step.artifacts) == 1 assert step.artifacts[0].artifact_id == "b225e278-8b4c-4f99-a696-8facf19f0e56" - assert step.is_last == False + assert step.is_last is False @pytest.mark.asyncio @@ -177,91 +199,67 @@ async def test_convert_to_artifact(): artifact = convert_to_artifact(artifact_model) assert artifact.artifact_id == "b225e278-8b4c-4f99-a696-8facf19f0e56" assert artifact.relative_path == "file:///path/to/main.py" - assert artifact.agent_created == True + assert artifact.agent_created is True @pytest.mark.asyncio -async def test_create_task(): - # Having issues with pytest fixture so added setup and teardown in each test as a rapid workaround - # TODO: Fix this! - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) - +async def test_create_task(agent_db: AgentDB): task = await agent_db.create_task("task_input") assert task.input == "task_input" - os.remove(db_name.split("///")[1]) @pytest.mark.asyncio -async def test_create_and_get_task(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) +async def test_create_and_get_task(agent_db: AgentDB): task = await agent_db.create_task("test_input") fetched_task = await agent_db.get_task(task.task_id) assert fetched_task.input == "test_input" - os.remove(db_name.split("///")[1]) @pytest.mark.asyncio -async def test_get_task_not_found(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) +async def test_get_task_not_found(agent_db: AgentDB): with pytest.raises(DataNotFoundError): - await agent_db.get_task(9999) - os.remove(db_name.split("///")[1]) + await agent_db.get_task("9999") @pytest.mark.asyncio -async def test_create_and_get_step(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) +async def test_create_and_get_step(agent_db: AgentDB): task = await agent_db.create_task("task_input") - step_input = StepInput(type="python/code") + step_input = {"type": "python/code"} request = StepRequestBody(input="test_input debug", additional_input=step_input) step = await agent_db.create_step(task.task_id, request) step = await agent_db.get_step(task.task_id, step.step_id) assert step.input == "test_input debug" - os.remove(db_name.split("///")[1]) @pytest.mark.asyncio -async def test_updating_step(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) +async def test_updating_step(agent_db: AgentDB): created_task = await agent_db.create_task("task_input") - step_input = StepInput(type="python/code") + step_input = {"type": "python/code"} request = StepRequestBody(input="test_input debug", additional_input=step_input) created_step = await agent_db.create_step(created_task.task_id, request) await agent_db.update_step(created_task.task_id, created_step.step_id, "completed") step = await agent_db.get_step(created_task.task_id, created_step.step_id) assert step.status.value == "completed" - os.remove(db_name.split("///")[1]) @pytest.mark.asyncio -async def test_get_step_not_found(): - db_name = "sqlite:///test_db.sqlite3" - agent_db = AgentDB(db_name) +async def test_get_step_not_found(agent_db: AgentDB): with pytest.raises(DataNotFoundError): - await agent_db.get_step(9999, 9999) - os.remove(db_name.split("///")[1]) + await agent_db.get_step("9999", "9999") @pytest.mark.asyncio -async def test_get_artifact(): - db_name = "sqlite:///test_db.sqlite3" - db = AgentDB(db_name) - +async def test_get_artifact(agent_db: AgentDB): # Given: A task and its corresponding artifact - task = await db.create_task("test_input debug") - step_input = StepInput(type="python/code") + task = await agent_db.create_task("test_input debug") + step_input = {"type": "python/code"} requst = StepRequestBody(input="test_input debug", additional_input=step_input) - step = await db.create_step(task.task_id, requst) + step = await agent_db.create_step(task.task_id, requst) # Create an artifact - artifact = await db.create_artifact( + artifact = await agent_db.create_artifact( task_id=task.task_id, file_name="test_get_artifact_sample_file.txt", relative_path="file:///path/to/test_get_artifact_sample_file.txt", @@ -270,7 +268,7 @@ async def test_get_artifact(): ) # When: The artifact is fetched by its ID - fetched_artifact = await db.get_artifact(artifact.artifact_id) + fetched_artifact = await agent_db.get_artifact(artifact.artifact_id) # Then: The fetched artifact matches the original assert fetched_artifact.artifact_id == artifact.artifact_id @@ -279,47 +277,37 @@ async def test_get_artifact(): == "file:///path/to/test_get_artifact_sample_file.txt" ) - os.remove(db_name.split("///")[1]) - @pytest.mark.asyncio -async def test_list_tasks(): - db_name = "sqlite:///test_db.sqlite3" - db = AgentDB(db_name) - +async def test_list_tasks(agent_db: AgentDB): # Given: Multiple tasks in the database - task1 = await db.create_task("test_input_1") - task2 = await db.create_task("test_input_2") + task1 = await agent_db.create_task("test_input_1") + task2 = await agent_db.create_task("test_input_2") # When: All tasks are fetched - fetched_tasks, pagination = await db.list_tasks() + fetched_tasks, pagination = await agent_db.list_tasks() # Then: The fetched tasks list includes the created tasks task_ids = [task.task_id for task in fetched_tasks] assert task1.task_id in task_ids assert task2.task_id in task_ids - os.remove(db_name.split("///")[1]) @pytest.mark.asyncio -async def test_list_steps(): - db_name = "sqlite:///test_db.sqlite3" - db = AgentDB(db_name) - - step_input = StepInput(type="python/code") - requst = StepRequestBody(input="test_input debug", additional_input=step_input) +async def test_list_steps(agent_db: AgentDB): + step_input = {"type": "python/code"} + request = StepRequestBody(input="test_input debug", additional_input=step_input) # Given: A task and multiple steps for that task - task = await db.create_task("test_input") - step1 = await db.create_step(task.task_id, requst) - requst = StepRequestBody(input="step two", additional_input=step_input) - step2 = await db.create_step(task.task_id, requst) + task = await agent_db.create_task("test_input") + step1 = await agent_db.create_step(task.task_id, request) + request = StepRequestBody(input="step two") + step2 = await agent_db.create_step(task.task_id, request) # When: All steps for the task are fetched - fetched_steps, pagination = await db.list_steps(task.task_id) + fetched_steps, pagination = await agent_db.list_steps(task.task_id) # Then: The fetched steps list includes the created steps step_ids = [step.step_id for step in fetched_steps] assert step1.step_id in step_ids assert step2.step_id in step_ids - os.remove(db_name.split("///")[1]) diff --git a/autogpts/forge/forge/sdk/middlewares.py b/forge/forge/agent_protocol/middlewares.py similarity index 86% rename from autogpts/forge/forge/sdk/middlewares.py rename to forge/forge/agent_protocol/middlewares.py index 6a204a01e2..a3e74ca820 100644 --- a/autogpts/forge/forge/sdk/middlewares.py +++ b/forge/forge/agent_protocol/middlewares.py @@ -1,9 +1,4 @@ -from typing import TYPE_CHECKING - -from fastapi import FastAPI - -if TYPE_CHECKING: - from .agent import Agent +from starlette.types import ASGIApp class AgentMiddleware: @@ -11,7 +6,7 @@ class AgentMiddleware: Middleware that injects the agent instance into the request scope. """ - def __init__(self, app: FastAPI, agent: "Agent"): + def __init__(self, app: ASGIApp, agent): """ Args: diff --git a/forge/forge/agent_protocol/models/__init__.py b/forge/forge/agent_protocol/models/__init__.py new file mode 100644 index 0000000000..f6fd0c4f3a --- /dev/null +++ b/forge/forge/agent_protocol/models/__init__.py @@ -0,0 +1,25 @@ +from .artifact import Artifact +from .pagination import Pagination +from .task import ( + Step, + StepRequestBody, + StepStatus, + Task, + TaskArtifactsListResponse, + TaskListResponse, + TaskRequestBody, + TaskStepsListResponse, +) + +__all__ = [ + "Artifact", + "Pagination", + "Step", + "StepRequestBody", + "StepStatus", + "Task", + "TaskArtifactsListResponse", + "TaskListResponse", + "TaskRequestBody", + "TaskStepsListResponse", +] diff --git a/forge/forge/agent_protocol/models/artifact.py b/forge/forge/agent_protocol/models/artifact.py new file mode 100644 index 0000000000..3219063bf9 --- /dev/null +++ b/forge/forge/agent_protocol/models/artifact.py @@ -0,0 +1,38 @@ +from datetime import datetime + +from pydantic import BaseModel, Field + + +class Artifact(BaseModel): + created_at: datetime = Field( + ..., + description="The creation datetime of the task.", + example="2023-01-01T00:00:00Z", + json_encoders={datetime: lambda v: v.isoformat()}, + ) + modified_at: datetime = Field( + ..., + description="The modification datetime of the task.", + example="2023-01-01T00:00:00Z", + json_encoders={datetime: lambda v: v.isoformat()}, + ) + artifact_id: str = Field( + ..., + description="ID of the artifact.", + example="b225e278-8b4c-4f99-a696-8facf19f0e56", + ) + agent_created: bool = Field( + ..., + description="Whether the artifact has been created by the agent.", + example=False, + ) + relative_path: str = Field( + ..., + description="Relative path of the artifact in the agents workspace.", + example="/my_folder/my_other_folder/", + ) + file_name: str = Field( + ..., + description="Filename of the artifact.", + example="main.py", + ) diff --git a/forge/forge/agent_protocol/models/pagination.py b/forge/forge/agent_protocol/models/pagination.py new file mode 100644 index 0000000000..1f77d97ae4 --- /dev/null +++ b/forge/forge/agent_protocol/models/pagination.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel, Field + + +class Pagination(BaseModel): + total_items: int = Field(..., description="Total number of items.", example=42) + total_pages: int = Field(..., description="Total number of pages.", example=97) + current_page: int = Field(..., description="Current_page page number.", example=1) + page_size: int = Field(..., description="Number of items per page.", example=25) diff --git a/autogpts/forge/forge/sdk/model.py b/forge/forge/agent_protocol/models/task.py similarity index 50% rename from autogpts/forge/forge/sdk/model.py rename to forge/forge/agent_protocol/models/task.py index 6a55b5a243..c7e30cbdbb 100644 --- a/autogpts/forge/forge/sdk/model.py +++ b/forge/forge/agent_protocol/models/task.py @@ -1,69 +1,13 @@ -# generated by fastapi-codegen: -# filename: ../../postman/schemas/openapi.yaml -# timestamp: 2023-08-25T10:36:11+00:00 - from __future__ import annotations from datetime import datetime from enum import Enum -from typing import List, Optional +from typing import Any, List, Optional from pydantic import BaseModel, Field - -class ArtifactUpload(BaseModel): - file: str = Field(..., description="File to upload.", format="binary") - relative_path: str = Field( - ..., - description="Relative path of the artifact in the agent's workspace.", - example="python/code", - ) - - -class Pagination(BaseModel): - total_items: int = Field(..., description="Total number of items.", example=42) - total_pages: int = Field(..., description="Total number of pages.", example=97) - current_page: int = Field(..., description="Current_page page number.", example=1) - page_size: int = Field(..., description="Number of items per page.", example=25) - - -class Artifact(BaseModel): - created_at: datetime = Field( - ..., - description="The creation datetime of the task.", - example="2023-01-01T00:00:00Z", - json_encoders={datetime: lambda v: v.isoformat()}, - ) - modified_at: datetime = Field( - ..., - description="The modification datetime of the task.", - example="2023-01-01T00:00:00Z", - json_encoders={datetime: lambda v: v.isoformat()}, - ) - artifact_id: str = Field( - ..., - description="ID of the artifact.", - example="b225e278-8b4c-4f99-a696-8facf19f0e56", - ) - agent_created: bool = Field( - ..., - description="Whether the artifact has been created by the agent.", - example=False, - ) - relative_path: str = Field( - ..., - description="Relative path of the artifact in the agents workspace.", - example="/my_folder/my_other_folder/", - ) - file_name: str = Field( - ..., - description="Filename of the artifact.", - example="main.py", - ) - - -class StepOutput(BaseModel): - pass +from .artifact import Artifact +from .pagination import Pagination class TaskRequestBody(BaseModel): @@ -73,7 +17,7 @@ class TaskRequestBody(BaseModel): description="Input prompt for the task.", example="Write the words you receive to the file 'output.txt'.", ) - additional_input: Optional[dict] = None + additional_input: dict[str, Any] = Field(default_factory=dict) class Task(TaskRequestBody): @@ -94,8 +38,8 @@ class Task(TaskRequestBody): description="The ID of the task.", example="50da533e-3904-4401-8a07-c49adf88b5eb", ) - artifacts: Optional[List[Artifact]] = Field( - [], + artifacts: list[Artifact] = Field( + default_factory=list, description="A list of artifacts that the task has produced.", example=[ "7a49f31c-f9c6-4346-a22c-e32bc5af4d8e", @@ -106,17 +50,15 @@ class Task(TaskRequestBody): class StepRequestBody(BaseModel): name: Optional[str] = Field( - None, description="The name of the task step.", example="Write to file" + default=None, description="The name of the task step.", example="Write to file" ) - input: Optional[str] = Field( - None, - description="Input prompt for the step.", - example="Washington", + input: str = Field( + ..., description="Input prompt for the step.", example="Washington" ) - additional_input: Optional[dict] = None + additional_input: dict[str, Any] = Field(default_factory=dict) -class Status(Enum): +class StepStatus(Enum): created = "created" running = "running" completed = "completed" @@ -146,19 +88,23 @@ class Step(StepRequestBody): example="6bb1801a-fd80-45e8-899a-4dd723cc602e", ) name: Optional[str] = Field( - None, description="The name of the task step.", example="Write to file" + default=None, description="The name of the task step.", example="Write to file" ) - status: Status = Field( + status: StepStatus = Field( ..., description="The status of the task step.", example="created" ) output: Optional[str] = Field( - None, + default=None, description="Output of the task step.", - example="I am going to use the write_to_file command and write Washington to a file called output.txt str: params = [ f"{param.name}: " - + ("%s" if param.spec.required else "Optional[%s]") % param.spec.type.value + + ("%s" if param.spec.required else "Optional[%s]") + % (param.spec.type.value if param.spec.type else "Any") for param in self.parameters ] return ( diff --git a/autogpts/forge/forge/command/decorator.py b/forge/forge/command/decorator.py similarity index 100% rename from autogpts/forge/forge/command/decorator.py rename to forge/forge/command/decorator.py diff --git a/autogpts/forge/forge/command/parameter.py b/forge/forge/command/parameter.py similarity index 81% rename from autogpts/forge/forge/command/parameter.py rename to forge/forge/command/parameter.py index 91e64a173a..c5101f3bb2 100644 --- a/autogpts/forge/forge/command/parameter.py +++ b/forge/forge/command/parameter.py @@ -1,10 +1,9 @@ -import dataclasses +from pydantic import BaseModel from forge.models.json_schema import JSONSchema -@dataclasses.dataclass -class CommandParameter: +class CommandParameter(BaseModel): name: str spec: JSONSchema diff --git a/autogpts/forge/forge/components/README.md b/forge/forge/components/README.md similarity index 100% rename from autogpts/forge/forge/components/README.md rename to forge/forge/components/README.md diff --git a/autogpts/forge/forge/components/action_history/__init__.py b/forge/forge/components/action_history/__init__.py similarity index 57% rename from autogpts/forge/forge/components/action_history/__init__.py rename to forge/forge/components/action_history/__init__.py index dfbb0e1bfa..d2fbc1ec2c 100644 --- a/autogpts/forge/forge/components/action_history/__init__.py +++ b/forge/forge/components/action_history/__init__.py @@ -1,2 +1,4 @@ from .action_history import ActionHistoryComponent from .model import Episode, EpisodicActionHistory + +__all__ = ["ActionHistoryComponent", "Episode", "EpisodicActionHistory"] diff --git a/autogpts/forge/forge/components/action_history/action_history.py b/forge/forge/components/action_history/action_history.py similarity index 81% rename from autogpts/forge/forge/components/action_history/action_history.py rename to forge/forge/components/action_history/action_history.py index 80a9aa2a79..db83e91d55 100644 --- a/autogpts/forge/forge/components/action_history/action_history.py +++ b/forge/forge/components/action_history/action_history.py @@ -1,25 +1,27 @@ -from typing import TYPE_CHECKING, Callable, Generic, Iterator, Optional +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Iterator, Optional from forge.agent.protocols import AfterExecute, AfterParse, MessageProvider from forge.llm.prompting.utils import indent -from forge.llm.providers import ChatMessage, ChatModelProvider +from forge.llm.providers import ChatMessage, MultiProvider if TYPE_CHECKING: from forge.config.config import Config -from .model import AP, ActionResult, Episode, EpisodicActionHistory +from .model import ActionResult, AnyProposal, Episode, EpisodicActionHistory -class ActionHistoryComponent(MessageProvider, AfterParse, AfterExecute, Generic[AP]): +class ActionHistoryComponent(MessageProvider, AfterParse[AnyProposal], AfterExecute): """Keeps track of the event history and provides a summary of the steps.""" def __init__( self, - event_history: EpisodicActionHistory[AP], + event_history: EpisodicActionHistory[AnyProposal], max_tokens: int, count_tokens: Callable[[str], int], - legacy_config: "Config", - llm_provider: ChatModelProvider, + legacy_config: Config, + llm_provider: MultiProvider, ) -> None: self.event_history = event_history self.max_tokens = max_tokens @@ -37,7 +39,7 @@ class ActionHistoryComponent(MessageProvider, AfterParse, AfterExecute, Generic[ f"## Progress on your Task so far\nThis is the list of the steps that you have executed previously, use this as your consideration on considering the next action!\n{progress}" ) - def after_parse(self, result: AP) -> None: + def after_parse(self, result: AnyProposal) -> None: self.event_history.register_action(result) async def after_execute(self, result: ActionResult) -> None: @@ -48,7 +50,7 @@ class ActionHistoryComponent(MessageProvider, AfterParse, AfterExecute, Generic[ def _compile_progress( self, - episode_history: list[Episode], + episode_history: list[Episode[AnyProposal]], max_tokens: Optional[int] = None, count_tokens: Optional[Callable[[str], int]] = None, ) -> str: diff --git a/autogpts/forge/forge/components/action_history/model.py b/forge/forge/components/action_history/model.py similarity index 87% rename from autogpts/forge/forge/components/action_history/model.py rename to forge/forge/components/action_history/model.py index cc24ad4b5e..37043af1c0 100644 --- a/autogpts/forge/forge/components/action_history/model.py +++ b/forge/forge/components/action_history/model.py @@ -1,25 +1,23 @@ from __future__ import annotations import asyncio -from typing import TYPE_CHECKING, Generic, Iterator, TypeVar +from typing import TYPE_CHECKING, Generic from pydantic import Field from pydantic.generics import GenericModel from forge.content_processing.text import summarize_text from forge.llm.prompting.utils import format_numbered_list, indent -from forge.models.action import ActionProposal, ActionResult +from forge.models.action import ActionResult, AnyProposal from forge.models.utils import ModelWithSummary if TYPE_CHECKING: from forge.config.config import Config - from forge.llm.providers import ChatModelProvider - -AP = TypeVar("AP", bound=ActionProposal) + from forge.llm.providers import MultiProvider -class Episode(GenericModel, Generic[AP]): - action: AP +class Episode(GenericModel, Generic[AnyProposal]): + action: AnyProposal result: ActionResult | None summary: str | None = None @@ -54,32 +52,29 @@ class Episode(GenericModel, Generic[AP]): return executed_action + action_result -class EpisodicActionHistory(GenericModel, Generic[AP]): +class EpisodicActionHistory(GenericModel, Generic[AnyProposal]): """Utility container for an action history""" - episodes: list[Episode[AP]] = Field(default_factory=list) + episodes: list[Episode[AnyProposal]] = Field(default_factory=list) cursor: int = 0 _lock = asyncio.Lock() @property - def current_episode(self) -> Episode[AP] | None: + def current_episode(self) -> Episode[AnyProposal] | None: if self.cursor == len(self): return None return self[self.cursor] - def __getitem__(self, key: int) -> Episode[AP]: + def __getitem__(self, key: int) -> Episode[AnyProposal]: return self.episodes[key] - def __iter__(self) -> Iterator[Episode[AP]]: - return iter(self.episodes) - def __len__(self) -> int: return len(self.episodes) def __bool__(self) -> bool: return len(self.episodes) > 0 - def register_action(self, action: AP) -> None: + def register_action(self, action: AnyProposal) -> None: if not self.current_episode: self.episodes.append(Episode(action=action, result=None)) assert self.current_episode @@ -113,7 +108,7 @@ class EpisodicActionHistory(GenericModel, Generic[AP]): self.cursor = len(self.episodes) async def handle_compression( - self, llm_provider: ChatModelProvider, app_config: Config + self, llm_provider: MultiProvider, app_config: Config ) -> None: """Compresses each episode in the action history using an LLM. diff --git a/forge/forge/components/code_executor/__init__.py b/forge/forge/components/code_executor/__init__.py new file mode 100644 index 0000000000..4d1c4804a4 --- /dev/null +++ b/forge/forge/components/code_executor/__init__.py @@ -0,0 +1,13 @@ +from .code_executor import ( + ALLOWLIST_CONTROL, + DENYLIST_CONTROL, + CodeExecutionError, + CodeExecutorComponent, +) + +__all__ = [ + "ALLOWLIST_CONTROL", + "DENYLIST_CONTROL", + "CodeExecutionError", + "CodeExecutorComponent", +] diff --git a/autogpts/forge/forge/components/code_executor/code_executor.py b/forge/forge/components/code_executor/code_executor.py similarity index 68% rename from autogpts/forge/forge/components/code_executor/code_executor.py rename to forge/forge/components/code_executor/code_executor.py index 346806deb4..0f738a7350 100644 --- a/autogpts/forge/forge/components/code_executor/code_executor.py +++ b/forge/forge/components/code_executor/code_executor.py @@ -1,22 +1,23 @@ import logging import os +import random import shlex +import string import subprocess from pathlib import Path -from tempfile import NamedTemporaryFile from typing import Iterator import docker from docker.errors import DockerException, ImageNotFound, NotFound from docker.models.containers import Container as DockerContainer -from forge.agent import BaseAgentSettings, CommandProvider +from forge.agent import BaseAgentSettings +from forge.agent.protocols import CommandProvider from forge.command import Command, command from forge.config.config import Config from forge.file_storage import FileStorage from forge.models.json_schema import JSONSchema from forge.utils.exceptions import ( - CodeExecutionError, CommandExecutionError, InvalidArgumentError, OperationNotAllowedError, @@ -51,6 +52,10 @@ def is_docker_available() -> bool: return False +class CodeExecutionError(CommandExecutionError): + """The operation (an attempt to run arbitrary code) returned an error""" + + class CodeExecutorComponent(CommandProvider): """Provides commands to execute Python code and shell commands.""" @@ -94,7 +99,7 @@ class CodeExecutorComponent(CommandProvider): ), }, ) - def execute_python_code(self, code: str) -> str: + async def execute_python_code(self, code: str) -> str: """ Create and execute a Python file in a Docker container and return the STDOUT of the executed code. @@ -110,18 +115,19 @@ class CodeExecutorComponent(CommandProvider): str: The STDOUT captured from the code when it ran. """ - tmp_code_file = NamedTemporaryFile( - "w", dir=self.workspace.root, suffix=".py", encoding="utf-8" - ) - tmp_code_file.write(code) - tmp_code_file.flush() + temp_path = "" + while True: + temp_path = f"temp{self._generate_random_string()}.py" + if not self.workspace.exists(temp_path): + break + await self.workspace.write_file(temp_path, code) try: - return self.execute_python_file(tmp_code_file.name) + return self.execute_python_file(temp_path) except Exception as e: raise CommandExecutionError(*e.args) finally: - tmp_code_file.close() + self.workspace.delete_file(temp_path) @command( ["execute_python_file"], @@ -141,7 +147,7 @@ class CodeExecutorComponent(CommandProvider): ), }, ) - def execute_python_file(self, filename: str, args: list[str] | str = []) -> str: + def execute_python_file(self, filename: str | Path, args: list[str] = []) -> str: """Execute a Python file in a Docker container and return the output Args: @@ -151,13 +157,7 @@ class CodeExecutorComponent(CommandProvider): Returns: str: The output of the file """ - logger.info( - f"Executing python file '{filename}' " - f"in working directory '{self.workspace.root}'" - ) - - if isinstance(args, str): - args = args.split() # Convert space-separated string to a list + logger.info(f"Executing python file '{filename}'") if not str(filename).endswith(".py"): raise InvalidArgumentError("Invalid file type. Only .py files are allowed.") @@ -176,98 +176,21 @@ class CodeExecutorComponent(CommandProvider): "App is running in a Docker container; " f"executing {file_path} directly..." ) - result = subprocess.run( - ["python", "-B", str(file_path)] + args, - capture_output=True, - encoding="utf8", - cwd=str(self.workspace.root), - ) - if result.returncode == 0: - return result.stdout - else: - raise CodeExecutionError(result.stderr) + with self.workspace.mount() as local_path: + result = subprocess.run( + ["python", "-B", str(file_path.relative_to(self.workspace.root))] + + args, + capture_output=True, + encoding="utf8", + cwd=str(local_path), + ) + if result.returncode == 0: + return result.stdout + else: + raise CodeExecutionError(result.stderr) logger.debug("App is not running in a Docker container") - try: - assert self.state.agent_id, "Need Agent ID to attach Docker container" - - client = docker.from_env() - image_name = "python:3-alpine" - container_is_fresh = False - container_name = f"{self.state.agent_id}_sandbox" - try: - container: DockerContainer = client.containers.get( - container_name - ) # type: ignore - except NotFound: - try: - client.images.get(image_name) - logger.debug(f"Image '{image_name}' found locally") - except ImageNotFound: - logger.info( - f"Image '{image_name}' not found locally," - " pulling from Docker Hub..." - ) - # Use the low-level API to stream the pull response - low_level_client = docker.APIClient() - for line in low_level_client.pull( - image_name, stream=True, decode=True - ): - # Print the status and progress, if available - status = line.get("status") - progress = line.get("progress") - if status and progress: - logger.info(f"{status}: {progress}") - elif status: - logger.info(status) - - logger.debug(f"Creating new {image_name} container...") - container: DockerContainer = client.containers.run( - image_name, - ["sleep", "60"], # Max 60 seconds to prevent permanent hangs - volumes={ - str(self.workspace.root): { - "bind": "/workspace", - "mode": "rw", - } - }, - working_dir="/workspace", - stderr=True, - stdout=True, - detach=True, - name=container_name, - ) # type: ignore - container_is_fresh = True - - if not container.status == "running": - container.start() - elif not container_is_fresh: - container.restart() - - logger.debug(f"Running {file_path} in container {container.name}...") - exec_result = container.exec_run( - [ - "python", - "-B", - file_path.relative_to(self.workspace.root).as_posix(), - ] - + args, - stderr=True, - stdout=True, - ) - - if exec_result.exit_code != 0: - raise CodeExecutionError(exec_result.output.decode("utf-8")) - - return exec_result.output.decode("utf-8") - - except DockerException as e: - logger.warning( - "Could not run the script in a container. " - "If you haven't already, please install Docker: " - "https://docs.docker.com/get-docker/" - ) - raise CommandExecutionError(f"Could not run the script in a container: {e}") + return self._run_python_code_in_docker(file_path, args) def validate_command(self, command_line: str, config: Config) -> tuple[bool, bool]: """Check whether a command is allowed and whether it may be executed in a shell. @@ -392,3 +315,96 @@ class CodeExecutorComponent(CommandProvider): os.chdir(current_dir) return f"Subprocess started with PID:'{str(process.pid)}'" + + def _run_python_code_in_docker(self, filename: str | Path, args: list[str]) -> str: + """Run a Python script in a Docker container""" + file_path = self.workspace.get_path(filename) + try: + assert self.state.agent_id, "Need Agent ID to attach Docker container" + + client = docker.from_env() + image_name = "python:3-alpine" + container_is_fresh = False + container_name = f"{self.state.agent_id}_sandbox" + with self.workspace.mount() as local_path: + try: + container: DockerContainer = client.containers.get( + container_name + ) # type: ignore + except NotFound: + try: + client.images.get(image_name) + logger.debug(f"Image '{image_name}' found locally") + except ImageNotFound: + logger.info( + f"Image '{image_name}' not found locally," + " pulling from Docker Hub..." + ) + # Use the low-level API to stream the pull response + low_level_client = docker.APIClient() + for line in low_level_client.pull( + image_name, stream=True, decode=True + ): + # Print the status and progress, if available + status = line.get("status") + progress = line.get("progress") + if status and progress: + logger.info(f"{status}: {progress}") + elif status: + logger.info(status) + + logger.debug(f"Creating new {image_name} container...") + container: DockerContainer = client.containers.run( + image_name, + ["sleep", "60"], # Max 60 seconds to prevent permanent hangs + volumes={ + str(local_path.resolve()): { + "bind": "/workspace", + "mode": "rw", + } + }, + working_dir="/workspace", + stderr=True, + stdout=True, + detach=True, + name=container_name, + ) # type: ignore + container_is_fresh = True + + if not container.status == "running": + container.start() + elif not container_is_fresh: + container.restart() + + logger.debug(f"Running {file_path} in container {container.name}...") + + exec_result = container.exec_run( + [ + "python", + "-B", + file_path.relative_to(self.workspace.root).as_posix(), + ] + + args, + stderr=True, + stdout=True, + ) + + if exec_result.exit_code != 0: + raise CodeExecutionError(exec_result.output.decode("utf-8")) + + return exec_result.output.decode("utf-8") + + except DockerException as e: + logger.warning( + "Could not run the script in a container. " + "If you haven't already, please install Docker: " + "https://docs.docker.com/get-docker/" + ) + raise CommandExecutionError(f"Could not run the script in a container: {e}") + + def _generate_random_string(self, length: int = 8): + # Create a string of all letters and digits + characters = string.ascii_letters + string.digits + # Use random.choices to generate a random string + random_string = "".join(random.choices(characters, k=length)) + return random_string diff --git a/autogpts/forge/forge/components/code_flow_executor/__init__.py b/forge/forge/components/code_flow_executor/__init__.py similarity index 100% rename from autogpts/forge/forge/components/code_flow_executor/__init__.py rename to forge/forge/components/code_flow_executor/__init__.py diff --git a/autogpts/forge/forge/components/code_flow_executor/code_flow_executor.py b/forge/forge/components/code_flow_executor/code_flow_executor.py similarity index 100% rename from autogpts/forge/forge/components/code_flow_executor/code_flow_executor.py rename to forge/forge/components/code_flow_executor/code_flow_executor.py diff --git a/autogpts/forge/forge/components/context/__init__.py b/forge/forge/components/context/__init__.py similarity index 53% rename from autogpts/forge/forge/components/context/__init__.py rename to forge/forge/components/context/__init__.py index 243746d524..703470f651 100644 --- a/autogpts/forge/forge/components/context/__init__.py +++ b/forge/forge/components/context/__init__.py @@ -5,3 +5,11 @@ from .context_item import ( FolderContextItem, StaticContextItem, ) + +__all__ = [ + "ContextComponent", + "ContextItem", + "FileContextItem", + "FolderContextItem", + "StaticContextItem", +] diff --git a/autogpts/forge/forge/components/context/context.py b/forge/forge/components/context/context.py similarity index 100% rename from autogpts/forge/forge/components/context/context.py rename to forge/forge/components/context/context.py diff --git a/autogpts/forge/forge/components/context/context_item.py b/forge/forge/components/context/context_item.py similarity index 100% rename from autogpts/forge/forge/components/context/context_item.py rename to forge/forge/components/context/context_item.py diff --git a/autogpts/forge/forge/components/file_manager/__init__.py b/forge/forge/components/file_manager/__init__.py similarity index 56% rename from autogpts/forge/forge/components/file_manager/__init__.py rename to forge/forge/components/file_manager/__init__.py index e0a1425299..d7a2b24402 100644 --- a/autogpts/forge/forge/components/file_manager/__init__.py +++ b/forge/forge/components/file_manager/__init__.py @@ -1 +1,3 @@ from .file_manager import FileManagerComponent + +__all__ = ["FileManagerComponent"] diff --git a/autogpts/forge/forge/components/file_manager/file_manager.py b/forge/forge/components/file_manager/file_manager.py similarity index 100% rename from autogpts/forge/forge/components/file_manager/file_manager.py rename to forge/forge/components/file_manager/file_manager.py diff --git a/autogpts/forge/forge/components/git_operations/__init__.py b/forge/forge/components/git_operations/__init__.py similarity index 57% rename from autogpts/forge/forge/components/git_operations/__init__.py rename to forge/forge/components/git_operations/__init__.py index 290f8e6153..d3a483d0a2 100644 --- a/autogpts/forge/forge/components/git_operations/__init__.py +++ b/forge/forge/components/git_operations/__init__.py @@ -1 +1,3 @@ from .git_operations import GitOperationsComponent + +__all__ = ["GitOperationsComponent"] diff --git a/autogpts/forge/forge/components/git_operations/git_operations.py b/forge/forge/components/git_operations/git_operations.py similarity index 100% rename from autogpts/forge/forge/components/git_operations/git_operations.py rename to forge/forge/components/git_operations/git_operations.py diff --git a/autogpts/forge/forge/components/image_gen/__init__.py b/forge/forge/components/image_gen/__init__.py similarity index 54% rename from autogpts/forge/forge/components/image_gen/__init__.py rename to forge/forge/components/image_gen/__init__.py index 00b8528d6a..84593d8ed8 100644 --- a/autogpts/forge/forge/components/image_gen/__init__.py +++ b/forge/forge/components/image_gen/__init__.py @@ -1 +1,3 @@ from .image_gen import ImageGeneratorComponent + +__all__ = ["ImageGeneratorComponent"] diff --git a/autogpts/forge/forge/components/image_gen/image_gen.py b/forge/forge/components/image_gen/image_gen.py similarity index 85% rename from autogpts/forge/forge/components/image_gen/image_gen.py rename to forge/forge/components/image_gen/image_gen.py index cf7d6ba3bd..45d155eea9 100644 --- a/autogpts/forge/forge/components/image_gen/image_gen.py +++ b/forge/forge/components/image_gen/image_gen.py @@ -32,7 +32,12 @@ class ImageGeneratorComponent(CommandProvider): self.legacy_config = config def get_commands(self) -> Iterator[Command]: - yield self.generate_image + if ( + self.legacy_config.openai_credentials + or self.legacy_config.huggingface_api_token + or self.legacy_config.sd_webui_auth + ): + yield self.generate_image @command( parameters={ @@ -60,17 +65,26 @@ class ImageGeneratorComponent(CommandProvider): str: The filename of the image """ filename = self.workspace.root / f"{str(uuid.uuid4())}.jpg" + cfg = self.legacy_config - # DALL-E - if self.legacy_config.image_provider == "dalle": + if cfg.openai_credentials and ( + cfg.image_provider == "dalle" + or not (cfg.huggingface_api_token or cfg.sd_webui_url) + ): return self.generate_image_with_dalle(prompt, filename, size) - # HuggingFace - elif self.legacy_config.image_provider == "huggingface": + + elif cfg.huggingface_api_token and ( + cfg.image_provider == "huggingface" + or not (cfg.openai_credentials or cfg.sd_webui_url) + ): return self.generate_image_with_hf(prompt, filename) - # SD WebUI - elif self.legacy_config.image_provider == "sdwebui": + + elif cfg.sd_webui_url and ( + cfg.image_provider == "sdwebui" or cfg.sd_webui_auth + ): return self.generate_image_with_sd_webui(prompt, filename, size) - return "No Image Provider Set" + + return "Error: No image generation provider available" def generate_image_with_hf(self, prompt: str, output_file: Path) -> str: """Generate an image with HuggingFace's API. @@ -142,6 +156,7 @@ class ImageGeneratorComponent(CommandProvider): Returns: str: The filename of the image """ + assert self.legacy_config.openai_credentials # otherwise this tool is disabled # Check for supported image sizes if size not in [256, 512, 1024]: @@ -152,16 +167,19 @@ class ImageGeneratorComponent(CommandProvider): ) size = closest + # TODO: integrate in `forge.llm.providers`(?) response = OpenAI( api_key=self.legacy_config.openai_credentials.api_key.get_secret_value() ).images.generate( prompt=prompt, n=1, - size=f"{size}x{size}", + # TODO: improve typing of size config item(s) + size=f"{size}x{size}", # type: ignore response_format="b64_json", ) + assert response.data[0].b64_json is not None # response_format = "b64_json" - logger.info(f"Image Generated for prompt:{prompt}") + logger.info(f"Image Generated for prompt: {prompt}") image_data = b64decode(response.data[0].b64_json) diff --git a/autogpts/forge/forge/components/system/__init__.py b/forge/forge/components/system/__init__.py similarity index 53% rename from autogpts/forge/forge/components/system/__init__.py rename to forge/forge/components/system/__init__.py index d6e654208e..3188d623e5 100644 --- a/autogpts/forge/forge/components/system/__init__.py +++ b/forge/forge/components/system/__init__.py @@ -1 +1,3 @@ from .system import SystemComponent + +__all__ = ["SystemComponent"] diff --git a/forge/forge/components/system/system.py b/forge/forge/components/system/system.py new file mode 100644 index 0000000000..32a269d966 --- /dev/null +++ b/forge/forge/components/system/system.py @@ -0,0 +1,79 @@ +import logging +import time +from typing import Iterator + +from forge.agent.protocols import CommandProvider, DirectiveProvider, MessageProvider +from forge.command import Command, command +from forge.llm.providers import ChatMessage +from forge.models.json_schema import JSONSchema +from forge.utils.const import FINISH_COMMAND +from forge.utils.exceptions import AgentFinished + +logger = logging.getLogger(__name__) + + +class SystemComponent(DirectiveProvider, MessageProvider, CommandProvider): + """Component for system messages and commands.""" + + def get_constraints(self) -> Iterator[str]: + yield "Exclusively use the commands listed below." + yield ( + "You can only act proactively, and are unable to start background jobs or " + "set up webhooks for yourself. " + "Take this into account when planning your actions." + ) + yield ( + "You are unable to interact with physical objects. " + "If this is absolutely necessary to fulfill a task or objective or " + "to complete a step, you must ask the user to do it for you. " + "If the user refuses this, and there is no other way to achieve your " + "goals, you must terminate to avoid wasting time and energy." + ) + + def get_resources(self) -> Iterator[str]: + yield ( + "You are a Large Language Model, trained on millions of pages of text, " + "including a lot of factual knowledge. Make use of this factual knowledge " + "to avoid unnecessary gathering of information." + ) + + def get_best_practices(self) -> Iterator[str]: + yield ( + "Continuously review and analyze your actions to ensure " + "you are performing to the best of your abilities." + ) + yield "Constructively self-criticize your big-picture behavior constantly." + yield "Reflect on past decisions and strategies to refine your approach." + yield ( + "Every command has a cost, so be smart and efficient. " + "Aim to complete tasks in the least number of steps." + ) + yield ( + "Only make use of your information gathering abilities to find " + "information that you don't yet have knowledge of." + ) + + def get_messages(self) -> Iterator[ChatMessage]: + # Clock + yield ChatMessage.system( + f"## Clock\nThe current time and date is {time.strftime('%c')}" + ) + + def get_commands(self) -> Iterator[Command]: + yield self.finish + + @command( + names=[FINISH_COMMAND], + parameters={ + "reason": JSONSchema( + type=JSONSchema.Type.STRING, + description="A summary to the user of how the goals were accomplished", + required=True, + ), + }, + ) + def finish(self, reason: str): + """Use this to shut down once you have completed your task, + or when there are insurmountable problems that make it impossible + for you to finish your task.""" + raise AgentFinished(reason) diff --git a/autogpts/forge/forge/components/user_interaction/__init__.py b/forge/forge/components/user_interaction/__init__.py similarity index 57% rename from autogpts/forge/forge/components/user_interaction/__init__.py rename to forge/forge/components/user_interaction/__init__.py index e354843c8a..1666441018 100644 --- a/autogpts/forge/forge/components/user_interaction/__init__.py +++ b/forge/forge/components/user_interaction/__init__.py @@ -1 +1,3 @@ from .user_interaction import UserInteractionComponent + +__all__ = ["UserInteractionComponent"] diff --git a/autogpts/forge/forge/components/user_interaction/user_interaction.py b/forge/forge/components/user_interaction/user_interaction.py similarity index 100% rename from autogpts/forge/forge/components/user_interaction/user_interaction.py rename to forge/forge/components/user_interaction/user_interaction.py diff --git a/autogpts/forge/forge/components/watchdog/__init__.py b/forge/forge/components/watchdog/__init__.py similarity index 54% rename from autogpts/forge/forge/components/watchdog/__init__.py rename to forge/forge/components/watchdog/__init__.py index d7c2196876..35cea1f88e 100644 --- a/autogpts/forge/forge/components/watchdog/__init__.py +++ b/forge/forge/components/watchdog/__init__.py @@ -1 +1,3 @@ from .watchdog import WatchdogComponent + +__all__ = ["WatchdogComponent"] diff --git a/autogpts/forge/forge/components/watchdog/watchdog.py b/forge/forge/components/watchdog/watchdog.py similarity index 86% rename from autogpts/forge/forge/components/watchdog/watchdog.py rename to forge/forge/components/watchdog/watchdog.py index 56406b0bf6..19eed2ec6f 100644 --- a/autogpts/forge/forge/components/watchdog/watchdog.py +++ b/forge/forge/components/watchdog/watchdog.py @@ -1,18 +1,20 @@ +from __future__ import annotations + import logging from typing import TYPE_CHECKING -if TYPE_CHECKING: - from forge.agent.base import BaseAgentConfiguration - from forge.agent.components import ComponentSystemError from forge.agent.protocols import AfterParse from forge.components.action_history import EpisodicActionHistory -from forge.models.action import ActionProposal +from forge.models.action import AnyProposal + +if TYPE_CHECKING: + from forge.agent.base import BaseAgentConfiguration logger = logging.getLogger(__name__) -class WatchdogComponent(AfterParse): +class WatchdogComponent(AfterParse[AnyProposal]): """ Adds a watchdog feature to an agent class. Whenever the agent starts looping, the watchdog will switch from the FAST_LLM to the SMART_LLM and re-think. @@ -21,13 +23,13 @@ class WatchdogComponent(AfterParse): def __init__( self, config: "BaseAgentConfiguration", - event_history: EpisodicActionHistory[ActionProposal], + event_history: EpisodicActionHistory[AnyProposal], ): self.config = config self.event_history = event_history self.revert_big_brain = False - def after_parse(self, result: ActionProposal) -> None: + def after_parse(self, result: AnyProposal) -> None: if self.revert_big_brain: self.config.big_brain = False self.revert_big_brain = False @@ -58,4 +60,4 @@ class WatchdogComponent(AfterParse): self.big_brain = True self.revert_big_brain = True # Trigger retry of all pipelines prior to this component - raise ComponentSystemError() + raise ComponentSystemError(rethink_reason, self) diff --git a/autogpts/forge/forge/components/web/__init__.py b/forge/forge/components/web/__init__.py similarity index 56% rename from autogpts/forge/forge/components/web/__init__.py rename to forge/forge/components/web/__init__.py index 2bcc86d10d..aca2f3f762 100644 --- a/autogpts/forge/forge/components/web/__init__.py +++ b/forge/forge/components/web/__init__.py @@ -1,2 +1,4 @@ from .search import WebSearchComponent from .selenium import BrowsingError, WebSeleniumComponent + +__all__ = ["WebSearchComponent", "BrowsingError", "WebSeleniumComponent"] diff --git a/autogpts/forge/forge/components/web/search.py b/forge/forge/components/web/search.py similarity index 100% rename from autogpts/forge/forge/components/web/search.py rename to forge/forge/components/web/search.py diff --git a/autogpts/forge/forge/components/web/selenium.py b/forge/forge/components/web/selenium.py similarity index 98% rename from autogpts/forge/forge/components/web/selenium.py rename to forge/forge/components/web/selenium.py index b5ba87119c..5a6c693dab 100644 --- a/autogpts/forge/forge/components/web/selenium.py +++ b/forge/forge/components/web/selenium.py @@ -12,7 +12,6 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions from selenium.webdriver.chrome.service import Service as ChromeDriverService from selenium.webdriver.chrome.webdriver import WebDriver as ChromeDriver from selenium.webdriver.common.by import By -from selenium.webdriver.common.options import ArgOptions as BrowserOptions from selenium.webdriver.edge.options import Options as EdgeOptions from selenium.webdriver.edge.service import Service as EdgeDriverService from selenium.webdriver.edge.webdriver import WebDriver as EdgeDriver @@ -33,7 +32,7 @@ from forge.command import Command, command from forge.config.config import Config from forge.content_processing.html import extract_hyperlinks, format_hyperlinks from forge.content_processing.text import extract_information, summarize_text -from forge.llm.providers.schema import ChatModelInfo, ChatModelProvider +from forge.llm.providers import ChatModelInfo, MultiProvider from forge.models.json_schema import JSONSchema from forge.utils.exceptions import CommandExecutionError, TooMuchOutputError from forge.utils.url_validator import validate_url @@ -45,6 +44,9 @@ MAX_RAW_CONTENT_LENGTH = 500 LINKS_TO_RETURN = 20 +BrowserOptions = ChromeOptions | EdgeOptions | FirefoxOptions | SafariOptions + + class BrowsingError(CommandExecutionError): """An error occurred while trying to browse the page""" @@ -55,7 +57,7 @@ class WebSeleniumComponent(DirectiveProvider, CommandProvider): def __init__( self, config: Config, - llm_provider: ChatModelProvider, + llm_provider: MultiProvider, model_info: ChatModelInfo, ): self.legacy_config = config @@ -251,7 +253,7 @@ class WebSeleniumComponent(DirectiveProvider, CommandProvider): if isinstance(options, FirefoxOptions): if config.selenium_headless: - options.headless = True + options.headless = True # type: ignore options.add_argument("--disable-gpu") driver = FirefoxDriver( service=GeckoDriverService(GeckoDriverManager().install()), diff --git a/autogpts/forge/forge/config/__init__.py b/forge/forge/config/__init__.py similarity index 100% rename from autogpts/forge/forge/config/__init__.py rename to forge/forge/config/__init__.py diff --git a/forge/forge/config/ai_directives.py b/forge/forge/config/ai_directives.py new file mode 100644 index 0000000000..2b6f0f0eaf --- /dev/null +++ b/forge/forge/config/ai_directives.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +import logging + +from pydantic import BaseModel, Field + +logger = logging.getLogger(__name__) + + +class AIDirectives(BaseModel): + """An object that contains the basic directives for the AI prompt. + + Attributes: + constraints (list): A list of constraints that the AI should adhere to. + resources (list): A list of resources that the AI can utilize. + best_practices (list): A list of best practices that the AI should follow. + """ + + resources: list[str] = Field(default_factory=list) + constraints: list[str] = Field(default_factory=list) + best_practices: list[str] = Field(default_factory=list) + + def __add__(self, other: AIDirectives) -> AIDirectives: + return AIDirectives( + resources=self.resources + other.resources, + constraints=self.constraints + other.constraints, + best_practices=self.best_practices + other.best_practices, + ).copy(deep=True) diff --git a/forge/forge/config/ai_profile.py b/forge/forge/config/ai_profile.py new file mode 100644 index 0000000000..a43d0bf495 --- /dev/null +++ b/forge/forge/config/ai_profile.py @@ -0,0 +1,27 @@ +from pydantic import BaseModel, Field + +DEFAULT_AI_NAME = "AutoGPT" +DEFAULT_AI_ROLE = ( + "a seasoned digital assistant: " + "capable, intelligent, considerate and assertive. " + "You have extensive research and development skills, and you don't shy " + "away from writing some code to solve a problem. " + "You are pragmatic and make the most out of the tools available to you." +) + + +class AIProfile(BaseModel): + """ + Object to hold the AI's personality. + + Attributes: + ai_name (str): The name of the AI. + ai_role (str): The description of the AI's role. + ai_goals (list): The list of objectives the AI is supposed to complete. + api_budget (float): The maximum dollar value for API calls (0.0 means infinite) + """ + + ai_name: str = DEFAULT_AI_NAME + ai_role: str = DEFAULT_AI_ROLE + """`ai_role` should fit in the following format: `You are {ai_name}, {ai_role}`""" + ai_goals: list[str] = Field(default_factory=list[str]) diff --git a/autogpts/forge/forge/config/config.py b/forge/forge/config/config.py similarity index 88% rename from autogpts/forge/forge/config/config.py rename to forge/forge/config/config.py index 0303210e16..27b57cb5a2 100644 --- a/autogpts/forge/forge/config/config.py +++ b/forge/forge/config/config.py @@ -22,9 +22,7 @@ from forge.speech.say import TTSConfig logger = logging.getLogger(__name__) PROJECT_ROOT = Path(forge.__file__).parent.parent -AI_SETTINGS_FILE = Path("ai_settings.yaml") AZURE_CONFIG_FILE = Path("azure.yaml") -PROMPT_SETTINGS_FILE = Path("prompt_settings.yaml") GPT_4_MODEL = OpenAIModelName.GPT4 GPT_3_MODEL = OpenAIModelName.GPT3 @@ -57,15 +55,6 @@ class Config(SystemSettings, arbitrary_types_allowed=True): ########################## # Agent Control Settings # ########################## - # Paths - ai_settings_file: Path = UserConfigurable( - default=AI_SETTINGS_FILE, from_env="AI_SETTINGS_FILE" - ) - prompt_settings_file: Path = UserConfigurable( - default=project_root / PROMPT_SETTINGS_FILE, - from_env="PROMPT_SETTINGS_FILE", - ) - # Model configuration fast_llm: ModelName = UserConfigurable( default=OpenAIModelName.GPT3, @@ -90,19 +79,6 @@ class Config(SystemSettings, arbitrary_types_allowed=True): continuous_mode: bool = False continuous_limit: int = 0 - ########## - # Memory # - ########## - memory_backend: str = UserConfigurable("json_file", from_env="MEMORY_BACKEND") - memory_index: str = UserConfigurable("auto-gpt-memory", from_env="MEMORY_INDEX") - redis_host: str = UserConfigurable("localhost", from_env="REDIS_HOST") - redis_port: int = UserConfigurable(default=6379, from_env="REDIS_PORT") - redis_password: str = UserConfigurable("", from_env="REDIS_PASSWORD") - wipe_redis_on_start: bool = UserConfigurable( - default=True, - from_env=lambda: os.getenv("WIPE_REDIS_ON_START", "True") == "True", - ) - ############ # Commands # ############ @@ -218,15 +194,13 @@ class ConfigBuilder(Configurable[Config]): # Make relative paths absolute for k in { - "ai_settings_file", # TODO: deprecate or repurpose - "prompt_settings_file", # TODO: deprecate or repurpose "azure_config_file", # TODO: move from project root }: setattr(config, k, project_root / getattr(config, k)) if ( config.openai_credentials - and config.openai_credentials.api_type == "azure" + and config.openai_credentials.api_type == SecretStr("azure") and (config_file := config.azure_config_file) ): config.openai_credentials.load_azure_config(config_file) diff --git a/forge/forge/conftest.py b/forge/forge/conftest.py new file mode 100644 index 0000000000..4a223b9a07 --- /dev/null +++ b/forge/forge/conftest.py @@ -0,0 +1,8 @@ +from pathlib import Path + +import pytest + + +@pytest.fixture() +def test_workspace(tmp_path: Path) -> Path: + return tmp_path diff --git a/autogpts/autogpt/tests/integration/__init__.py b/forge/forge/content_processing/__init__.py similarity index 100% rename from autogpts/autogpt/tests/integration/__init__.py rename to forge/forge/content_processing/__init__.py diff --git a/autogpts/forge/forge/content_processing/html.py b/forge/forge/content_processing/html.py similarity index 100% rename from autogpts/forge/forge/content_processing/html.py rename to forge/forge/content_processing/html.py diff --git a/autogpts/forge/forge/content_processing/text.py b/forge/forge/content_processing/text.py similarity index 95% rename from autogpts/forge/forge/content_processing/text.py rename to forge/forge/content_processing/text.py index 02e6392c95..8fe6a46a3d 100644 --- a/autogpts/forge/forge/content_processing/text.py +++ b/forge/forge/content_processing/text.py @@ -1,4 +1,5 @@ """Text processing functions""" +from __future__ import annotations import logging import math @@ -11,7 +12,7 @@ if TYPE_CHECKING: from forge.json.parsing import extract_list_from_json from forge.llm.prompting import ChatPrompt -from forge.llm.providers import ChatMessage, ChatModelProvider, ModelTokenizer +from forge.llm.providers import ChatMessage, ModelTokenizer, MultiProvider logger = logging.getLogger(__name__) @@ -55,8 +56,8 @@ def chunk_content( async def summarize_text( text: str, - llm_provider: ChatModelProvider, - config: "Config", + llm_provider: MultiProvider, + config: Config, question: Optional[str] = None, instruction: Optional[str] = None, ) -> tuple[str, list[tuple[str, str]]]: @@ -88,8 +89,8 @@ async def summarize_text( async def extract_information( source_text: str, topics_of_interest: list[str], - llm_provider: ChatModelProvider, - config: "Config", + llm_provider: MultiProvider, + config: Config, ) -> list[str]: fmt_topics_list = "\n".join(f"* {topic}." for topic in topics_of_interest) instruction = ( @@ -112,8 +113,8 @@ async def extract_information( async def _process_text( text: str, instruction: str, - llm_provider: ChatModelProvider, - config: "Config", + llm_provider: MultiProvider, + config: Config, output_type: type[str | list[str]] = str, ) -> tuple[str, list[tuple[str, str]]] | list[str]: """Process text using the OpenAI API for summarization or information extraction @@ -122,7 +123,7 @@ async def _process_text( text (str): The text to process. instruction (str): Additional instruction for processing. llm_provider: LLM provider to use. - config ("Config"): The global application config. + config (Config): The global application config. output_type: `str` for summaries or `list[str]` for piece-wise info extraction. Returns: @@ -164,7 +165,7 @@ async def _process_text( ), ) - if output_type == list[str]: + if isinstance(response.parsed_result, list): logger.debug(f"Raw LLM response: {repr(response.response.content)}") fmt_result_bullet_list = "\n".join(f"* {r}" for r in response.parsed_result) logger.debug( @@ -220,7 +221,7 @@ async def _process_text( def split_text( text: str, - config: "Config", + config: Config, max_chunk_length: int, tokenizer: ModelTokenizer, with_overlap: bool = True, @@ -230,7 +231,7 @@ def split_text( Args: text (str): The text to split. - config ("Config"): "Config" object containing the Spacy model setting. + config (Config): Config object containing the Spacy model setting. max_chunk_length (int, optional): The maximum length of a chunk. tokenizer (ModelTokenizer): Tokenizer to use for determining chunk length. with_overlap (bool, optional): Whether to allow overlap between chunks. diff --git a/autogpts/forge/forge/file_storage/__init__.py b/forge/forge/file_storage/__init__.py similarity index 81% rename from autogpts/forge/forge/file_storage/__init__.py rename to forge/forge/file_storage/__init__.py index 9d5df553e2..7623a93125 100644 --- a/autogpts/forge/forge/file_storage/__init__.py +++ b/forge/forge/file_storage/__init__.py @@ -1,10 +1,7 @@ import enum from pathlib import Path -from .base import FileStorage, FileStorageConfiguration -from .gcs import GCSFileStorage, GCSFileStorageConfiguration -from .local import LocalFileStorage -from .s3 import S3FileStorage, S3FileStorageConfiguration +from .base import FileStorage class FileStorageBackendName(str, enum.Enum): @@ -15,7 +12,7 @@ class FileStorageBackendName(str, enum.Enum): def get_storage( backend: FileStorageBackendName, - root_path: Path = ".", + root_path: Path = Path("."), restrict_to_root: bool = True, ) -> FileStorage: match backend: diff --git a/autogpts/forge/forge/file_storage/base.py b/forge/forge/file_storage/base.py similarity index 63% rename from autogpts/forge/forge/file_storage/base.py rename to forge/forge/file_storage/base.py index 822c46e43f..8c16bcc4c3 100644 --- a/autogpts/forge/forge/file_storage/base.py +++ b/forge/forge/file_storage/base.py @@ -4,12 +4,18 @@ The FileStorage class provides an interface for interacting with a file storage. from __future__ import annotations +import asyncio import logging import os +import shutil +import tempfile from abc import ABC, abstractmethod -from io import IOBase, TextIOBase +from contextlib import contextmanager from pathlib import Path -from typing import IO, Any, BinaryIO, Callable, Literal, TextIO, overload +from typing import Any, BinaryIO, Callable, Generator, Literal, TextIO, overload + +from watchdog.events import FileSystemEvent, FileSystemEventHandler +from watchdog.observers import Observer from forge.models.config import SystemConfiguration @@ -60,26 +66,29 @@ class FileStorage(ABC): def open_file( self, path: str | Path, - mode: Literal["w", "r"] = "r", + mode: Literal["r", "w"] = "r", binary: Literal[False] = False, - ) -> TextIO | TextIOBase: + ) -> TextIO: """Returns a readable text file-like object representing the file.""" @overload @abstractmethod def open_file( - self, - path: str | Path, - mode: Literal["w", "r"] = "r", - binary: Literal[True] = True, - ) -> BinaryIO | IOBase: + self, path: str | Path, mode: Literal["r", "w"], binary: Literal[True] + ) -> BinaryIO: + """Returns a binary file-like object representing the file.""" + + @overload + @abstractmethod + def open_file(self, path: str | Path, *, binary: Literal[True]) -> BinaryIO: """Returns a readable binary file-like object representing the file.""" + @overload @abstractmethod def open_file( - self, path: str | Path, mode: Literal["w", "r"] = "r", binary: bool = False - ) -> IO | IOBase: - """Returns a readable file-like object representing the file.""" + self, path: str | Path, mode: Literal["r", "w"] = "r", binary: bool = False + ) -> TextIO | BinaryIO: + """Returns a file-like object representing the file.""" @overload @abstractmethod @@ -89,13 +98,15 @@ class FileStorage(ABC): @overload @abstractmethod - def read_file(self, path: str | Path, binary: Literal[True] = True) -> bytes: + def read_file(self, path: str | Path, binary: Literal[True]) -> bytes: """Read a file in the storage as binary.""" ... + @overload @abstractmethod def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: """Read a file in the storage.""" + ... @abstractmethod async def write_file(self, path: str | Path, content: str | bytes) -> None: @@ -150,6 +161,32 @@ class FileStorage(ABC): """ return self._sanitize_path(relative_path) + @contextmanager + def mount(self, path: str | Path = ".") -> Generator[Path, Any, None]: + """Mount the file storage and provide a local path.""" + local_path = tempfile.mkdtemp(dir=path) + + observer = Observer() + try: + # Copy all files to the local directory + files = self.list_files() + for file in files: + file_path = local_path / file + file_path.parent.mkdir(parents=True, exist_ok=True) + content = self.read_file(file, binary=True) + file_path.write_bytes(content) + + # Sync changes + event_handler = FileSyncHandler(self, local_path) + observer.schedule(event_handler, local_path, recursive=True) + observer.start() + + yield Path(local_path) + finally: + observer.stop() + observer.join() + shutil.rmtree(local_path) + def _sanitize_path( self, path: str | Path, @@ -202,3 +239,45 @@ class FileStorage(ABC): ) return full_path + + +class FileSyncHandler(FileSystemEventHandler): + def __init__(self, storage: FileStorage, path: str | Path = "."): + self.storage = storage + self.path = Path(path) + + def on_modified(self, event: FileSystemEvent): + if event.is_directory: + return + + file_path = Path(event.src_path).relative_to(self.path) + content = file_path.read_bytes() + # Must execute write_file synchronously because the hook is synchronous + # TODO: Schedule write operation using asyncio.create_task (non-blocking) + asyncio.get_event_loop().run_until_complete( + self.storage.write_file(file_path, content) + ) + + def on_created(self, event: FileSystemEvent): + if event.is_directory: + self.storage.make_dir(event.src_path) + return + + file_path = Path(event.src_path).relative_to(self.path) + content = file_path.read_bytes() + # Must execute write_file synchronously because the hook is synchronous + # TODO: Schedule write operation using asyncio.create_task (non-blocking) + asyncio.get_event_loop().run_until_complete( + self.storage.write_file(file_path, content) + ) + + def on_deleted(self, event: FileSystemEvent): + if event.is_directory: + self.storage.delete_dir(event.src_path) + return + + file_path = event.src_path + self.storage.delete_file(file_path) + + def on_moved(self, event: FileSystemEvent): + self.storage.rename(event.src_path, event.dest_path) diff --git a/autogpts/forge/forge/file_storage/gcs.py b/forge/forge/file_storage/gcs.py similarity index 82% rename from autogpts/forge/forge/file_storage/gcs.py rename to forge/forge/file_storage/gcs.py index f631cd81b9..010ea29331 100644 --- a/autogpts/forge/forge/file_storage/gcs.py +++ b/forge/forge/file_storage/gcs.py @@ -7,12 +7,13 @@ from __future__ import annotations import inspect import logging -from io import IOBase +from io import TextIOWrapper from pathlib import Path -from typing import Literal +from typing import Literal, overload from google.cloud import storage from google.cloud.exceptions import NotFound +from google.cloud.storage.fileio import BlobReader, BlobWriter from forge.models.config import UserConfigurable @@ -73,14 +74,67 @@ class GCSFileStorage(FileStorage): path = self.get_path(path) return self._bucket.blob(str(path)) + @overload def open_file( - self, path: str | Path, mode: Literal["w", "r"] = "r", binary: bool = False - ) -> IOBase: + self, + path: str | Path, + mode: Literal["r", "w"] = "r", + binary: Literal[False] = False, + ) -> TextIOWrapper: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["r"], binary: Literal[True] + ) -> BlobReader: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["w"], binary: Literal[True] + ) -> BlobWriter: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["r", "w"], binary: Literal[True] + ) -> BlobWriter | BlobReader: + ... + + @overload + def open_file(self, path: str | Path, *, binary: Literal[True]) -> BlobReader: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["r", "w"] = "r", binary: bool = False + ) -> BlobReader | BlobWriter | TextIOWrapper: + ... + + # https://github.com/microsoft/pyright/issues/8007 + def open_file( # pyright: ignore[reportIncompatibleMethodOverride] + self, path: str | Path, mode: Literal["r", "w"] = "r", binary: bool = False + ) -> BlobReader | BlobWriter | TextIOWrapper: """Open a file in the storage.""" blob = self._get_blob(path) blob.reload() # pin revision number to prevent version mixing while reading return blob.open(f"{mode}b" if binary else mode) + @overload + def read_file(self, path: str | Path, binary: Literal[False] = False) -> str: + """Read a file in the storage as text.""" + ... + + @overload + def read_file(self, path: str | Path, binary: Literal[True]) -> bytes: + """Read a file in the storage as binary.""" + ... + + @overload + def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: + """Read a file in the storage.""" + ... + def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: """Read a file in the storage.""" return self.open_file(path, "r", binary).read() diff --git a/autogpts/forge/forge/file_storage/local.py b/forge/forge/file_storage/local.py similarity index 74% rename from autogpts/forge/forge/file_storage/local.py rename to forge/forge/file_storage/local.py index 3a52bd5724..06591a18e5 100644 --- a/autogpts/forge/forge/file_storage/local.py +++ b/forge/forge/file_storage/local.py @@ -6,8 +6,9 @@ from __future__ import annotations import inspect import logging +from contextlib import contextmanager from pathlib import Path -from typing import IO, Literal +from typing import Any, BinaryIO, Generator, Literal, TextIO, overload from .base import FileStorage, FileStorageConfiguration @@ -41,16 +42,58 @@ class LocalFileStorage(FileStorage): def initialize(self) -> None: self.root.mkdir(exist_ok=True, parents=True) + @overload + def open_file( + self, + path: str | Path, + mode: Literal["w", "r"] = "r", + binary: Literal[False] = False, + ) -> TextIO: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["w", "r"], binary: Literal[True] + ) -> BinaryIO: + ... + + @overload + def open_file(self, path: str | Path, *, binary: Literal[True]) -> BinaryIO: + ... + + @overload def open_file( self, path: str | Path, mode: Literal["w", "r"] = "r", binary: bool = False - ) -> IO: + ) -> TextIO | BinaryIO: + ... + + def open_file( + self, path: str | Path, mode: Literal["w", "r"] = "r", binary: bool = False + ) -> TextIO | BinaryIO: """Open a file in the storage.""" return self._open_file(path, f"{mode}b" if binary else mode) - def _open_file(self, path: str | Path, mode: str) -> IO: + def _open_file(self, path: str | Path, mode: str) -> TextIO | BinaryIO: full_path = self.get_path(path) + if any(m in mode for m in ("w", "a", "x")): + full_path.parent.mkdir(parents=True, exist_ok=True) return open(full_path, mode) # type: ignore + @overload + def read_file(self, path: str | Path, binary: Literal[False] = False) -> str: + """Read a file in the storage as text.""" + ... + + @overload + def read_file(self, path: str | Path, binary: Literal[True]) -> bytes: + """Read a file in the storage as binary.""" + ... + + @overload + def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: + """Read a file in the storage.""" + ... + def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: """Read a file in the storage.""" with self._open_file(path, "rb" if binary else "r") as file: @@ -59,7 +102,7 @@ class LocalFileStorage(FileStorage): async def write_file(self, path: str | Path, content: str | bytes) -> None: """Write to a file in the storage.""" with self._open_file(path, "wb" if type(content) is bytes else "w") as file: - file.write(content) + file.write(content) # type: ignore if self.on_write_file: path = Path(path) @@ -137,3 +180,9 @@ class LocalFileStorage(FileStorage): restrict_to_root=self.restrict_to_root, ) ) + + @contextmanager + def mount(self, path: str | Path = ".") -> Generator[Path, Any, None]: + """Mount the file storage and provide a local path.""" + # No need to do anything for local storage + yield Path(self.get_path(".")).absolute() diff --git a/autogpts/forge/forge/file_storage/s3.py b/forge/forge/file_storage/s3.py similarity index 77% rename from autogpts/forge/forge/file_storage/s3.py rename to forge/forge/file_storage/s3.py index c7cfd8bafa..b7f04d1abf 100644 --- a/autogpts/forge/forge/file_storage/s3.py +++ b/forge/forge/file_storage/s3.py @@ -8,9 +8,9 @@ from __future__ import annotations import contextlib import inspect import logging -from io import IOBase, TextIOWrapper +from io import TextIOWrapper from pathlib import Path -from typing import TYPE_CHECKING, Literal, Optional +from typing import TYPE_CHECKING, BinaryIO, Literal, Optional, overload import boto3 import botocore.exceptions @@ -22,6 +22,7 @@ from .base import FileStorage, FileStorageConfiguration if TYPE_CHECKING: import mypy_boto3_s3 + from botocore.response import StreamingBody logger = logging.getLogger(__name__) @@ -89,18 +90,60 @@ class S3FileStorage(FileStorage): def _get_obj(self, path: str | Path) -> mypy_boto3_s3.service_resource.Object: """Get an S3 object.""" - path = self.get_path(path) obj = self._bucket.Object(str(path)) with contextlib.suppress(botocore.exceptions.ClientError): obj.load() return obj + @overload def open_file( - self, path: str | Path, mode: Literal["w", "r"] = "r", binary: bool = False - ) -> IOBase: + self, + path: str | Path, + mode: Literal["r", "w"] = "r", + binary: Literal[False] = False, + ) -> TextIOWrapper: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["r", "w"], binary: Literal[True] + ) -> S3BinaryIOWrapper: + ... + + @overload + def open_file( + self, path: str | Path, *, binary: Literal[True] + ) -> S3BinaryIOWrapper: + ... + + @overload + def open_file( + self, path: str | Path, mode: Literal["r", "w"] = "r", binary: bool = False + ) -> S3BinaryIOWrapper | TextIOWrapper: + ... + + def open_file( + self, path: str | Path, mode: Literal["r", "w"] = "r", binary: bool = False + ) -> TextIOWrapper | S3BinaryIOWrapper: """Open a file in the storage.""" - obj = self._get_obj(path) - return obj.get()["Body"] if binary else TextIOWrapper(obj.get()["Body"]) + path = self.get_path(path) + body = S3BinaryIOWrapper(self._get_obj(path).get()["Body"], str(path)) + return body if binary else TextIOWrapper(body) + + @overload + def read_file(self, path: str | Path, binary: Literal[False] = False) -> str: + """Read a file in the storage as text.""" + ... + + @overload + def read_file(self, path: str | Path, binary: Literal[True]) -> bytes: + """Read a file in the storage as binary.""" + ... + + @overload + def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: + """Read a file in the storage.""" + ... def read_file(self, path: str | Path, binary: bool = False) -> str | bytes: """Read a file in the storage.""" @@ -108,7 +151,7 @@ class S3FileStorage(FileStorage): async def write_file(self, path: str | Path, content: str | bytes) -> None: """Write to a file in the storage.""" - obj = self._get_obj(path) + obj = self._get_obj(self.get_path(path)) obj.put(Body=content) if self.on_write_file: @@ -172,7 +215,7 @@ class S3FileStorage(FileStorage): self._s3.meta.client.head_object(Bucket=self._bucket_name, Key=str(path)) return True except botocore.exceptions.ClientError as e: - if int(e.response["ResponseMetadata"]["HTTPStatusCode"]) == 404: + if e.response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 404: # If the object does not exist, # check for objects with the prefix (folder) prefix = f"{str(path).rstrip('/')}/" @@ -201,7 +244,7 @@ class S3FileStorage(FileStorage): ) self._s3.meta.client.delete_object(Bucket=self._bucket_name, Key=old_path) except botocore.exceptions.ClientError as e: - if int(e.response["ResponseMetadata"]["HTTPStatusCode"]) == 404: + if e.response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 404: # If the object does not exist, # it may be a folder prefix = f"{old_path.rstrip('/')}/" @@ -233,7 +276,7 @@ class S3FileStorage(FileStorage): Key=destination, ) except botocore.exceptions.ClientError as e: - if int(e.response["ResponseMetadata"]["HTTPStatusCode"]) == 404: + if e.response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 404: # If the object does not exist, # it may be a folder prefix = f"{source.rstrip('/')}/" @@ -254,7 +297,7 @@ class S3FileStorage(FileStorage): S3FileStorageConfiguration( bucket=self._bucket_name, root=Path("/").joinpath(self.get_path(subroot)), - s3_endpoint_url=self._s3.meta.client.meta.endpoint_url, + s3_endpoint_url=SecretStr(self._s3.meta.client.meta.endpoint_url), ) ) file_storage._s3 = self._s3 @@ -263,3 +306,48 @@ class S3FileStorage(FileStorage): def __repr__(self) -> str: return f"{__class__.__name__}(bucket='{self._bucket_name}', root={self._root})" + + +class S3BinaryIOWrapper(BinaryIO): + def __init__(self, body: StreamingBody, name: str): + self.body = body + self._name = name + + @property + def name(self) -> str: + return self._name + + def read(self, size: int = -1) -> bytes: + return self.body.read(size if size > 0 else None) + + def readinto(self, b: bytearray) -> int: + data = self.read(len(b)) + b[: len(data)] = data + return len(data) + + def close(self) -> None: + self.body.close() + + def fileno(self) -> int: + return self.body.fileno() + + def flush(self) -> None: + self.body.flush() + + def isatty(self) -> bool: + return self.body.isatty() + + def readable(self) -> bool: + return self.body.readable() + + def seekable(self) -> bool: + return self.body.seekable() + + def writable(self) -> bool: + return False + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.body.close() diff --git a/autogpts/autogpt/tests/memory/__init__.py b/forge/forge/json/__init__.py similarity index 100% rename from autogpts/autogpt/tests/memory/__init__.py rename to forge/forge/json/__init__.py diff --git a/autogpts/forge/forge/json/parsing.py b/forge/forge/json/parsing.py similarity index 100% rename from autogpts/forge/forge/json/parsing.py rename to forge/forge/json/parsing.py diff --git a/autogpts/autogpt/tests/mocks/__init__.py b/forge/forge/llm/__init__.py similarity index 100% rename from autogpts/autogpt/tests/mocks/__init__.py rename to forge/forge/llm/__init__.py diff --git a/autogpts/forge/forge/llm/prompting/__init__.py b/forge/forge/llm/prompting/__init__.py similarity index 100% rename from autogpts/forge/forge/llm/prompting/__init__.py rename to forge/forge/llm/prompting/__init__.py diff --git a/autogpts/forge/forge/llm/prompting/base.py b/forge/forge/llm/prompting/base.py similarity index 66% rename from autogpts/forge/forge/llm/prompting/base.py rename to forge/forge/llm/prompting/base.py index 0bc3517865..475bdc6405 100644 --- a/autogpts/forge/forge/llm/prompting/base.py +++ b/forge/forge/llm/prompting/base.py @@ -1,7 +1,5 @@ import abc -from typing import TYPE_CHECKING - -from forge.models.config import SystemConfiguration +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from forge.llm.providers import AssistantChatMessage @@ -10,8 +8,6 @@ from .schema import ChatPrompt, LanguageModelClassification class PromptStrategy(abc.ABC): - default_configuration: SystemConfiguration - @property @abc.abstractmethod def model_classification(self) -> LanguageModelClassification: @@ -22,5 +18,5 @@ class PromptStrategy(abc.ABC): ... @abc.abstractmethod - def parse_response_content(self, response_content: "AssistantChatMessage"): + def parse_response_content(self, response: "AssistantChatMessage") -> Any: ... diff --git a/autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/role_selection.j2 b/forge/forge/llm/prompting/gpt-3.5-turbo/role_selection.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/role_selection.j2 rename to forge/forge/llm/prompting/gpt-3.5-turbo/role_selection.j2 diff --git a/autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/system-format.j2 b/forge/forge/llm/prompting/gpt-3.5-turbo/system-format.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/system-format.j2 rename to forge/forge/llm/prompting/gpt-3.5-turbo/system-format.j2 diff --git a/autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/task-step.j2 b/forge/forge/llm/prompting/gpt-3.5-turbo/task-step.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/gpt-3.5-turbo/task-step.j2 rename to forge/forge/llm/prompting/gpt-3.5-turbo/task-step.j2 diff --git a/autogpts/forge/forge/llm/prompting/schema.py b/forge/forge/llm/prompting/schema.py similarity index 93% rename from autogpts/forge/forge/llm/prompting/schema.py rename to forge/forge/llm/prompting/schema.py index 7dabfd9bfc..0b0008a379 100644 --- a/autogpts/forge/forge/llm/prompting/schema.py +++ b/forge/forge/llm/prompting/schema.py @@ -27,7 +27,7 @@ class ChatPrompt(BaseModel): prefill_response: str = "" def raw(self) -> list[ChatMessageDict]: - return [m.dict() for m in self.messages] + return [m.dict() for m in self.messages] # type: ignore def __str__(self): return "\n\n".join( diff --git a/autogpts/forge/forge/llm/prompting/techniques/chain-of-thought.j2 b/forge/forge/llm/prompting/techniques/chain-of-thought.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/techniques/chain-of-thought.j2 rename to forge/forge/llm/prompting/techniques/chain-of-thought.j2 diff --git a/autogpts/forge/forge/llm/prompting/techniques/expert.j2 b/forge/forge/llm/prompting/techniques/expert.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/techniques/expert.j2 rename to forge/forge/llm/prompting/techniques/expert.j2 diff --git a/autogpts/forge/forge/llm/prompting/techniques/few-shot.j2 b/forge/forge/llm/prompting/techniques/few-shot.j2 similarity index 100% rename from autogpts/forge/forge/llm/prompting/techniques/few-shot.j2 rename to forge/forge/llm/prompting/techniques/few-shot.j2 diff --git a/autogpts/forge/forge/llm/prompting/utils.py b/forge/forge/llm/prompting/utils.py similarity index 52% rename from autogpts/forge/forge/llm/prompting/utils.py rename to forge/forge/llm/prompting/utils.py index 4d45dec3f7..89fdef4d26 100644 --- a/autogpts/forge/forge/llm/prompting/utils.py +++ b/forge/forge/llm/prompting/utils.py @@ -1,5 +1,26 @@ +from math import ceil, floor from typing import Any +from forge.llm.prompting.schema import ChatPrompt + +SEPARATOR_LENGTH = 42 + + +def dump_prompt(prompt: ChatPrompt) -> str: + def separator(text: str): + half_sep_len = (SEPARATOR_LENGTH - 2 - len(text)) / 2 + return f"{floor(half_sep_len)*'-'} {text.upper()} {ceil(half_sep_len)*'-'}" + + formatted_messages = "\n".join( + [f"{separator(m.role)}\n{m.content}" for m in prompt.messages] + ) + return f""" +============== {prompt.__class__.__name__} ============== +Length: {len(prompt.messages)} messages +{formatted_messages} +========================================== +""" + def format_numbered_list(items: list[Any], start_at: int = 1) -> str: return "\n".join(f"{i}. {str(item)}" for i, item in enumerate(items, start_at)) diff --git a/autogpts/forge/forge/llm/providers/__init__.py b/forge/forge/llm/providers/__init__.py similarity index 94% rename from autogpts/forge/forge/llm/providers/__init__.py rename to forge/forge/llm/providers/__init__.py index 82c4e110f0..aeac6281e8 100644 --- a/autogpts/forge/forge/llm/providers/__init__.py +++ b/forge/forge/llm/providers/__init__.py @@ -1,4 +1,10 @@ -from .multi import CHAT_MODELS, ModelName, MultiProvider +from .multi import ( + CHAT_MODELS, + ChatModelProvider, + EmbeddingModelProvider, + ModelName, + MultiProvider, +) from .openai import ( OPEN_AI_CHAT_MODELS, OPEN_AI_EMBEDDING_MODELS, @@ -14,15 +20,12 @@ from .schema import ( AssistantFunctionCallDict, ChatMessage, ChatModelInfo, - ChatModelProvider, ChatModelResponse, CompletionModelFunction, Embedding, EmbeddingModelInfo, - EmbeddingModelProvider, EmbeddingModelResponse, ModelInfo, - ModelProvider, ModelProviderBudget, ModelProviderCredentials, ModelProviderName, @@ -41,7 +44,6 @@ __all__ = [ "AssistantFunctionCallDict", "ChatMessage", "ChatModelInfo", - "ChatModelProvider", "ChatModelResponse", "CompletionModelFunction", "CHAT_MODELS", @@ -51,7 +53,7 @@ __all__ = [ "EmbeddingModelResponse", "ModelInfo", "ModelName", - "ModelProvider", + "ChatModelProvider", "ModelProviderBudget", "ModelProviderCredentials", "ModelProviderName", diff --git a/forge/forge/llm/providers/_openai_base.py b/forge/forge/llm/providers/_openai_base.py new file mode 100644 index 0000000000..ce2ef77868 --- /dev/null +++ b/forge/forge/llm/providers/_openai_base.py @@ -0,0 +1,517 @@ +import inspect +import logging +from typing import ( + Any, + Awaitable, + Callable, + ClassVar, + Mapping, + Optional, + ParamSpec, + Sequence, + TypeVar, + cast, +) + +import sentry_sdk +import tenacity +from openai._exceptions import APIConnectionError, APIStatusError +from openai.types import CreateEmbeddingResponse, EmbeddingCreateParams +from openai.types.chat import ( + ChatCompletion, + ChatCompletionAssistantMessageParam, + ChatCompletionMessage, + ChatCompletionMessageParam, + CompletionCreateParams, +) +from openai.types.shared_params import FunctionDefinition + +from forge.json.parsing import json_loads + +from .schema import ( + AssistantChatMessage, + AssistantFunctionCall, + AssistantToolCall, + BaseChatModelProvider, + BaseEmbeddingModelProvider, + BaseModelProvider, + ChatMessage, + ChatModelInfo, + ChatModelResponse, + CompletionModelFunction, + Embedding, + EmbeddingModelInfo, + EmbeddingModelResponse, + ModelProviderService, + _ModelName, + _ModelProviderSettings, +) +from .utils import validate_tool_calls + +_T = TypeVar("_T") +_P = ParamSpec("_P") + + +class _BaseOpenAIProvider(BaseModelProvider[_ModelName, _ModelProviderSettings]): + """Base class for LLM providers with OpenAI-like APIs""" + + MODELS: ClassVar[ + Mapping[_ModelName, ChatModelInfo[_ModelName] | EmbeddingModelInfo[_ModelName]] # type: ignore # noqa + ] + + def __init__( + self, + settings: Optional[_ModelProviderSettings] = None, + logger: Optional[logging.Logger] = None, + ): + if not getattr(self, "MODELS", None): + raise ValueError(f"{self.__class__.__name__}.MODELS is not set") + + if not settings: + settings = self.default_settings.copy(deep=True) + if not settings.credentials: + settings.credentials = self.default_settings.__fields__[ + "credentials" + ].type_.from_env() + + super(_BaseOpenAIProvider, self).__init__(settings=settings, logger=logger) + + if not getattr(self, "_client", None): + from openai import AsyncOpenAI + + self._client = AsyncOpenAI( + **self._credentials.get_api_access_kwargs() # type: ignore + ) + + async def get_available_models( + self, + ) -> Sequence[ChatModelInfo[_ModelName] | EmbeddingModelInfo[_ModelName]]: + _models = (await self._client.models.list()).data + return [ + self.MODELS[cast(_ModelName, m.id)] for m in _models if m.id in self.MODELS + ] + + def get_token_limit(self, model_name: _ModelName) -> int: + """Get the maximum number of input tokens for a given model""" + return self.MODELS[model_name].max_tokens + + def count_tokens(self, text: str, model_name: _ModelName) -> int: + return len(self.get_tokenizer(model_name).encode(text)) + + def _retry_api_request(self, func: Callable[_P, _T]) -> Callable[_P, _T]: + return tenacity.retry( + retry=( + tenacity.retry_if_exception_type(APIConnectionError) + | tenacity.retry_if_exception( + lambda e: isinstance(e, APIStatusError) and e.status_code >= 500 + ) + ), + wait=tenacity.wait_exponential(), + stop=tenacity.stop_after_attempt(self._configuration.retries_per_request), + after=tenacity.after_log(self._logger, logging.DEBUG), + )(func) + + def __repr__(self): + return f"{self.__class__.__name__}()" + + +class BaseOpenAIChatProvider( + _BaseOpenAIProvider[_ModelName, _ModelProviderSettings], + BaseChatModelProvider[_ModelName, _ModelProviderSettings], +): + CHAT_MODELS: ClassVar[dict[_ModelName, ChatModelInfo[_ModelName]]] # type: ignore + + def __init__( + self, + settings: Optional[_ModelProviderSettings] = None, + logger: Optional[logging.Logger] = None, + ): + if not getattr(self, "CHAT_MODELS", None): + raise ValueError(f"{self.__class__.__name__}.CHAT_MODELS is not set") + + super(BaseOpenAIChatProvider, self).__init__(settings=settings, logger=logger) + + async def get_available_chat_models(self) -> Sequence[ChatModelInfo[_ModelName]]: + all_available_models = await self.get_available_models() + return [ + model + for model in all_available_models + if model.service == ModelProviderService.CHAT + ] + + def count_message_tokens( + self, + messages: ChatMessage | list[ChatMessage], + model_name: _ModelName, + ) -> int: + if isinstance(messages, ChatMessage): + messages = [messages] + return self.count_tokens( + "\n\n".join(f"{m.role.upper()}: {m.content}" for m in messages), model_name + ) + + async def create_chat_completion( + self, + model_prompt: list[ChatMessage], + model_name: _ModelName, + completion_parser: Callable[[AssistantChatMessage], _T] = lambda _: None, + functions: Optional[list[CompletionModelFunction]] = None, + max_output_tokens: Optional[int] = None, + prefill_response: str = "", + **kwargs, + ) -> ChatModelResponse[_T]: + """Create a chat completion using the API.""" + + ( + openai_messages, + completion_kwargs, + parse_kwargs, + ) = self._get_chat_completion_args( + prompt_messages=model_prompt, + model=model_name, + functions=functions, + max_output_tokens=max_output_tokens, + **kwargs, + ) + + total_cost = 0.0 + attempts = 0 + while True: + completion_kwargs["messages"] = openai_messages + _response, _cost, t_input, t_output = await self._create_chat_completion( + model=model_name, + completion_kwargs=completion_kwargs, + ) + total_cost += _cost + + # If parsing the response fails, append the error to the prompt, and let the + # LLM fix its mistake(s). + attempts += 1 + parse_errors: list[Exception] = [] + + _assistant_msg = _response.choices[0].message + + tool_calls, _errors = self._parse_assistant_tool_calls( + _assistant_msg, **parse_kwargs + ) + parse_errors += _errors + + # Validate tool calls + if not parse_errors and tool_calls and functions: + parse_errors += validate_tool_calls(tool_calls, functions) + + assistant_msg = AssistantChatMessage( + content=_assistant_msg.content or "", + tool_calls=tool_calls or None, + ) + + parsed_result: _T = None # type: ignore + if not parse_errors: + try: + parsed_result = completion_parser(assistant_msg) + if inspect.isawaitable(parsed_result): + parsed_result = await parsed_result + except Exception as e: + parse_errors.append(e) + + if not parse_errors: + if attempts > 1: + self._logger.debug( + f"Total cost for {attempts} attempts: ${round(total_cost, 5)}" + ) + + return ChatModelResponse( + response=AssistantChatMessage( + content=_assistant_msg.content or "", + tool_calls=tool_calls or None, + ), + parsed_result=parsed_result, + model_info=self.CHAT_MODELS[model_name], + prompt_tokens_used=t_input, + completion_tokens_used=t_output, + ) + + else: + self._logger.debug( + f"Parsing failed on response: '''{_assistant_msg}'''" + ) + parse_errors_fmt = "\n\n".join( + f"{e.__class__.__name__}: {e}" for e in parse_errors + ) + self._logger.warning( + f"Parsing attempt #{attempts} failed: {parse_errors_fmt}" + ) + for e in parse_errors: + sentry_sdk.capture_exception( + error=e, + extras={"assistant_msg": _assistant_msg, "i_attempt": attempts}, + ) + + if attempts < self._configuration.fix_failed_parse_tries: + openai_messages.append( + cast( + ChatCompletionAssistantMessageParam, + _assistant_msg.dict(exclude_none=True), + ) + ) + openai_messages.append( + { + "role": "system", + "content": ( + f"ERROR PARSING YOUR RESPONSE:\n\n{parse_errors_fmt}" + ), + } + ) + continue + else: + raise parse_errors[0] + + def _get_chat_completion_args( + self, + prompt_messages: list[ChatMessage], + model: _ModelName, + functions: Optional[list[CompletionModelFunction]] = None, + max_output_tokens: Optional[int] = None, + **kwargs, + ) -> tuple[ + list[ChatCompletionMessageParam], CompletionCreateParams, dict[str, Any] + ]: + """Prepare keyword arguments for a chat completion API call + + Args: + prompt_messages: List of ChatMessages + model: The model to use + functions (optional): List of functions available to the LLM + max_output_tokens (optional): Maximum number of tokens to generate + + Returns: + list[ChatCompletionMessageParam]: Prompt messages for the API call + CompletionCreateParams: Mapping of other kwargs for the API call + Mapping[str, Any]: Any keyword arguments to pass on to the completion parser + """ + kwargs = cast(CompletionCreateParams, kwargs) + + if max_output_tokens: + kwargs["max_tokens"] = max_output_tokens + + if functions: + kwargs["tools"] = [ # pyright: ignore - it fails to infer the dict type + {"type": "function", "function": format_function_def_for_openai(f)} + for f in functions + ] + if len(functions) == 1: + # force the model to call the only specified function + kwargs["tool_choice"] = { # pyright: ignore - type inference failure + "type": "function", + "function": {"name": functions[0].name}, + } + + if extra_headers := self._configuration.extra_request_headers: + # 'extra_headers' is not on CompletionCreateParams, but is on chat.create() + kwargs["extra_headers"] = kwargs.get("extra_headers", {}) # type: ignore + kwargs["extra_headers"].update(extra_headers.copy()) # type: ignore + + prepped_messages: list[ChatCompletionMessageParam] = [ + message.dict( # type: ignore + include={"role", "content", "tool_calls", "tool_call_id", "name"}, + exclude_none=True, + ) + for message in prompt_messages + ] + + if "messages" in kwargs: + prepped_messages += kwargs["messages"] + del kwargs["messages"] # type: ignore - messages are added back later + + return prepped_messages, kwargs, {} + + async def _create_chat_completion( + self, + model: _ModelName, + completion_kwargs: CompletionCreateParams, + ) -> tuple[ChatCompletion, float, int, int]: + """ + Create a chat completion using an OpenAI-like API with retry handling + + Params: + model: The model to use for the completion + completion_kwargs: All other arguments for the completion call + + Returns: + ChatCompletion: The chat completion response object + float: The cost ($) of this completion + int: Number of prompt tokens used + int: Number of completion tokens used + """ + completion_kwargs["model"] = completion_kwargs.get("model") or model + + @self._retry_api_request + async def _create_chat_completion_with_retry() -> ChatCompletion: + return await self._client.chat.completions.create( + **completion_kwargs, # type: ignore + ) + + completion = await _create_chat_completion_with_retry() + + if completion.usage: + prompt_tokens_used = completion.usage.prompt_tokens + completion_tokens_used = completion.usage.completion_tokens + else: + prompt_tokens_used = completion_tokens_used = 0 + + if self._budget: + cost = self._budget.update_usage_and_cost( + model_info=self.CHAT_MODELS[model], + input_tokens_used=prompt_tokens_used, + output_tokens_used=completion_tokens_used, + ) + else: + cost = 0 + + self._logger.debug( + f"{model} completion usage: {prompt_tokens_used} input, " + f"{completion_tokens_used} output - ${round(cost, 5)}" + ) + return completion, cost, prompt_tokens_used, completion_tokens_used + + def _parse_assistant_tool_calls( + self, assistant_message: ChatCompletionMessage, **kwargs + ) -> tuple[list[AssistantToolCall], list[Exception]]: + tool_calls: list[AssistantToolCall] = [] + parse_errors: list[Exception] = [] + + if assistant_message.tool_calls: + for _tc in assistant_message.tool_calls: + try: + parsed_arguments = json_loads(_tc.function.arguments) + except Exception as e: + err_message = ( + f"Decoding arguments for {_tc.function.name} failed: " + + str(e.args[0]) + ) + parse_errors.append( + type(e)(err_message, *e.args[1:]).with_traceback( + e.__traceback__ + ) + ) + continue + + tool_calls.append( + AssistantToolCall( + id=_tc.id, + type=_tc.type, + function=AssistantFunctionCall( + name=_tc.function.name, + arguments=parsed_arguments, + ), + ) + ) + + # If parsing of all tool calls succeeds in the end, we ignore any issues + if len(tool_calls) == len(assistant_message.tool_calls): + parse_errors = [] + + return tool_calls, parse_errors + + +class BaseOpenAIEmbeddingProvider( + _BaseOpenAIProvider[_ModelName, _ModelProviderSettings], + BaseEmbeddingModelProvider[_ModelName, _ModelProviderSettings], +): + EMBEDDING_MODELS: ClassVar[ + dict[_ModelName, EmbeddingModelInfo[_ModelName]] # type: ignore + ] + + def __init__( + self, + settings: Optional[_ModelProviderSettings] = None, + logger: Optional[logging.Logger] = None, + ): + if not getattr(self, "EMBEDDING_MODELS", None): + raise ValueError(f"{self.__class__.__name__}.EMBEDDING_MODELS is not set") + + super(BaseOpenAIEmbeddingProvider, self).__init__( + settings=settings, logger=logger + ) + + async def get_available_embedding_models( + self, + ) -> Sequence[EmbeddingModelInfo[_ModelName]]: + all_available_models = await self.get_available_models() + return [ + model + for model in all_available_models + if model.service == ModelProviderService.EMBEDDING + ] + + async def create_embedding( + self, + text: str, + model_name: _ModelName, + embedding_parser: Callable[[Embedding], Embedding], + **kwargs, + ) -> EmbeddingModelResponse: + """Create an embedding using an OpenAI-like API""" + embedding_kwargs = self._get_embedding_kwargs( + input=text, model=model_name, **kwargs + ) + response = await self._create_embedding(embedding_kwargs) + + return EmbeddingModelResponse( + embedding=embedding_parser(response.data[0].embedding), + model_info=self.EMBEDDING_MODELS[model_name], + prompt_tokens_used=response.usage.prompt_tokens, + ) + + def _get_embedding_kwargs( + self, input: str | list[str], model: _ModelName, **kwargs + ) -> EmbeddingCreateParams: + """Get kwargs for an embedding API call + + Params: + input: Text body or list of text bodies to create embedding(s) from + model: Embedding model to use + + Returns: + The kwargs for the embedding API call + """ + kwargs = cast(EmbeddingCreateParams, kwargs) + + kwargs["input"] = input + kwargs["model"] = model + + if extra_headers := self._configuration.extra_request_headers: + # 'extra_headers' is not on CompletionCreateParams, but is on embedding.create() # noqa + kwargs["extra_headers"] = kwargs.get("extra_headers", {}) # type: ignore + kwargs["extra_headers"].update(extra_headers.copy()) # type: ignore + + return kwargs + + def _create_embedding( + self, embedding_kwargs: EmbeddingCreateParams + ) -> Awaitable[CreateEmbeddingResponse]: + """Create an embedding using an OpenAI-like API with retry handling.""" + + @self._retry_api_request + async def _create_embedding_with_retry() -> CreateEmbeddingResponse: + return await self._client.embeddings.create(**embedding_kwargs) + + return _create_embedding_with_retry() + + +def format_function_def_for_openai(self: CompletionModelFunction) -> FunctionDefinition: + """Returns an OpenAI-consumable function definition""" + + return { + "name": self.name, + "description": self.description, + "parameters": { + "type": "object", + "properties": { + name: param.to_dict() for name, param in self.parameters.items() + }, + "required": [ + name for name, param in self.parameters.items() if param.required + ], + }, + } diff --git a/autogpts/forge/forge/llm/providers/anthropic.py b/forge/forge/llm/providers/anthropic.py similarity index 90% rename from autogpts/forge/forge/llm/providers/anthropic.py rename to forge/forge/llm/providers/anthropic.py index 74b516451f..2f8d21571d 100644 --- a/autogpts/forge/forge/llm/providers/anthropic.py +++ b/forge/forge/llm/providers/anthropic.py @@ -2,7 +2,7 @@ from __future__ import annotations import enum import logging -from typing import TYPE_CHECKING, Callable, Optional, ParamSpec, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Optional, ParamSpec, Sequence, TypeVar import sentry_sdk import tenacity @@ -10,13 +10,15 @@ import tiktoken from anthropic import APIConnectionError, APIStatusError from pydantic import SecretStr -from forge.llm.providers.schema import ( +from forge.models.config import UserConfigurable + +from .schema import ( AssistantChatMessage, AssistantFunctionCall, AssistantToolCall, + BaseChatModelProvider, ChatMessage, ChatModelInfo, - ChatModelProvider, ChatModelResponse, CompletionModelFunction, ModelProviderBudget, @@ -27,8 +29,6 @@ from forge.llm.providers.schema import ( ModelTokenizer, ToolResultMessage, ) -from forge.models.config import Configurable, UserConfigurable - from .utils import validate_tool_calls if TYPE_CHECKING: @@ -77,21 +77,17 @@ ANTHROPIC_CHAT_MODELS = { } -class AnthropicConfiguration(ModelProviderConfiguration): - fix_failed_parse_tries: int = UserConfigurable(3) - - class AnthropicCredentials(ModelProviderCredentials): """Credentials for Anthropic.""" - api_key: SecretStr = UserConfigurable(from_env="ANTHROPIC_API_KEY") + api_key: SecretStr = UserConfigurable(from_env="ANTHROPIC_API_KEY") # type: ignore api_base: Optional[SecretStr] = UserConfigurable( default=None, from_env="ANTHROPIC_API_BASE_URL" ) def get_api_access_kwargs(self) -> dict[str, str]: return { - k: (v.get_secret_value() if type(v) is SecretStr else v) + k: v.get_secret_value() for k, v in { "api_key": self.api_key, "base_url": self.api_base, @@ -101,24 +97,20 @@ class AnthropicCredentials(ModelProviderCredentials): class AnthropicSettings(ModelProviderSettings): - configuration: AnthropicConfiguration - credentials: Optional[AnthropicCredentials] - budget: ModelProviderBudget + credentials: Optional[AnthropicCredentials] # type: ignore + budget: ModelProviderBudget # type: ignore -class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): +class AnthropicProvider(BaseChatModelProvider[AnthropicModelName, AnthropicSettings]): default_settings = AnthropicSettings( name="anthropic_provider", description="Provides access to Anthropic's API.", - configuration=AnthropicConfiguration( - retries_per_request=7, - ), + configuration=ModelProviderConfiguration(), credentials=None, budget=ModelProviderBudget(), ) _settings: AnthropicSettings - _configuration: AnthropicConfiguration _credentials: AnthropicCredentials _budget: ModelProviderBudget @@ -136,27 +128,31 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): from anthropic import AsyncAnthropic - self._client = AsyncAnthropic(**self._credentials.get_api_access_kwargs()) + self._client = AsyncAnthropic( + **self._credentials.get_api_access_kwargs() # type: ignore + ) - async def get_available_models(self) -> list[ChatModelInfo]: + async def get_available_models(self) -> Sequence[ChatModelInfo[AnthropicModelName]]: + return await self.get_available_chat_models() + + async def get_available_chat_models( + self, + ) -> Sequence[ChatModelInfo[AnthropicModelName]]: return list(ANTHROPIC_CHAT_MODELS.values()) - def get_token_limit(self, model_name: str) -> int: + def get_token_limit(self, model_name: AnthropicModelName) -> int: """Get the token limit for a given model.""" return ANTHROPIC_CHAT_MODELS[model_name].max_tokens - @classmethod - def get_tokenizer(cls, model_name: AnthropicModelName) -> ModelTokenizer: + def get_tokenizer(self, model_name: AnthropicModelName) -> ModelTokenizer[Any]: # HACK: No official tokenizer is available for Claude 3 return tiktoken.encoding_for_model(model_name) - @classmethod - def count_tokens(cls, text: str, model_name: AnthropicModelName) -> int: + def count_tokens(self, text: str, model_name: AnthropicModelName) -> int: return 0 # HACK: No official tokenizer is available for Claude 3 - @classmethod def count_message_tokens( - cls, + self, messages: ChatMessage | list[ChatMessage], model_name: AnthropicModelName, ) -> int: @@ -195,7 +191,7 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): cost, t_input, t_output, - ) = await self._create_chat_completion(completion_kwargs) + ) = await self._create_chat_completion(model_name, completion_kwargs) total_cost += cost self._logger.debug( f"Completion usage: {t_input} input, {t_output} output " @@ -245,7 +241,7 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): ) if attempts < self._configuration.fix_failed_parse_tries: anthropic_messages.append( - _assistant_msg.dict(include={"role", "content"}) + _assistant_msg.dict(include={"role", "content"}) # type: ignore ) anthropic_messages.append( { @@ -312,7 +308,6 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): def _get_chat_completion_args( self, prompt_messages: list[ChatMessage], - model: AnthropicModelName, functions: Optional[list[CompletionModelFunction]] = None, max_output_tokens: Optional[int] = None, **kwargs, @@ -321,7 +316,6 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): Args: prompt_messages: List of ChatMessages. - model: The model to use. functions: Optional list of functions available to the LLM. kwargs: Additional keyword arguments. @@ -329,8 +323,6 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): list[MessageParam]: Prompt messages for the Anthropic call dict[str, Any]: Any other kwargs for the Anthropic call """ - kwargs["model"] = model - if functions: kwargs["tools"] = [ { @@ -433,7 +425,7 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): return messages, kwargs # type: ignore async def _create_chat_completion( - self, completion_kwargs: MessageCreateParams + self, model: AnthropicModelName, completion_kwargs: MessageCreateParams ) -> tuple[Message, float, int, int]: """ Create a chat completion using the Anthropic API with retry handling. @@ -449,17 +441,15 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): """ @self._retry_api_request - async def _create_chat_completion_with_retry( - completion_kwargs: MessageCreateParams, - ) -> Message: + async def _create_chat_completion_with_retry() -> Message: return await self._client.beta.tools.messages.create( - **completion_kwargs # type: ignore + model=model, **completion_kwargs # type: ignore ) - response = await _create_chat_completion_with_retry(completion_kwargs) + response = await _create_chat_completion_with_retry() cost = self._budget.update_usage_and_cost( - model_info=ANTHROPIC_CHAT_MODELS[completion_kwargs["model"]], + model_info=ANTHROPIC_CHAT_MODELS[model], input_tokens_used=response.usage.input_tokens, output_tokens_used=response.usage.output_tokens, ) @@ -472,7 +462,10 @@ class AnthropicProvider(Configurable[AnthropicSettings], ChatModelProvider): AssistantToolCall( id=c.id, type="function", - function=AssistantFunctionCall(name=c.name, arguments=c.input), + function=AssistantFunctionCall( + name=c.name, + arguments=c.input, # type: ignore + ), ) for c in assistant_message.content if c.type == "tool_use" diff --git a/forge/forge/llm/providers/groq.py b/forge/forge/llm/providers/groq.py new file mode 100644 index 0000000000..dc9e77e0b8 --- /dev/null +++ b/forge/forge/llm/providers/groq.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import enum +import logging +from typing import Any, Optional + +import tiktoken +from pydantic import SecretStr + +from forge.models.config import UserConfigurable + +from ._openai_base import BaseOpenAIChatProvider +from .schema import ( + ChatModelInfo, + ModelProviderBudget, + ModelProviderConfiguration, + ModelProviderCredentials, + ModelProviderName, + ModelProviderSettings, + ModelTokenizer, +) + + +class GroqModelName(str, enum.Enum): + LLAMA3_8B = "llama3-8b-8192" + LLAMA3_70B = "llama3-70b-8192" + MIXTRAL_8X7B = "mixtral-8x7b-32768" + GEMMA_7B = "gemma-7b-it" + + +GROQ_CHAT_MODELS = { + info.name: info + for info in [ + ChatModelInfo( + name=GroqModelName.LLAMA3_8B, + provider_name=ModelProviderName.GROQ, + prompt_token_cost=0.05 / 1e6, + completion_token_cost=0.10 / 1e6, + max_tokens=8192, + has_function_call_api=True, + ), + ChatModelInfo( + name=GroqModelName.LLAMA3_70B, + provider_name=ModelProviderName.GROQ, + prompt_token_cost=0.59 / 1e6, + completion_token_cost=0.79 / 1e6, + max_tokens=8192, + has_function_call_api=True, + ), + ChatModelInfo( + name=GroqModelName.MIXTRAL_8X7B, + provider_name=ModelProviderName.GROQ, + prompt_token_cost=0.27 / 1e6, + completion_token_cost=0.27 / 1e6, + max_tokens=32768, + has_function_call_api=True, + ), + ChatModelInfo( + name=GroqModelName.GEMMA_7B, + provider_name=ModelProviderName.GROQ, + prompt_token_cost=0.10 / 1e6, + completion_token_cost=0.10 / 1e6, + max_tokens=8192, + has_function_call_api=True, + ), + ] +} + + +class GroqCredentials(ModelProviderCredentials): + """Credentials for Groq.""" + + api_key: SecretStr = UserConfigurable(from_env="GROQ_API_KEY") # type: ignore + api_base: Optional[SecretStr] = UserConfigurable( + default=None, from_env="GROQ_API_BASE_URL" + ) + + def get_api_access_kwargs(self) -> dict[str, str]: + return { + k: v.get_secret_value() + for k, v in { + "api_key": self.api_key, + "base_url": self.api_base, + }.items() + if v is not None + } + + +class GroqSettings(ModelProviderSettings): + credentials: Optional[GroqCredentials] # type: ignore + budget: ModelProviderBudget # type: ignore + + +class GroqProvider(BaseOpenAIChatProvider[GroqModelName, GroqSettings]): + CHAT_MODELS = GROQ_CHAT_MODELS + MODELS = CHAT_MODELS + + default_settings = GroqSettings( + name="groq_provider", + description="Provides access to Groq's API.", + configuration=ModelProviderConfiguration(), + credentials=None, + budget=ModelProviderBudget(), + ) + + _settings: GroqSettings + _configuration: ModelProviderConfiguration + _credentials: GroqCredentials + _budget: ModelProviderBudget + + def __init__( + self, + settings: Optional[GroqSettings] = None, + logger: Optional[logging.Logger] = None, + ): + super(GroqProvider, self).__init__(settings=settings, logger=logger) + + from groq import AsyncGroq + + self._client = AsyncGroq( + **self._credentials.get_api_access_kwargs() # type: ignore + ) + + def get_tokenizer(self, model_name: GroqModelName) -> ModelTokenizer[Any]: + # HACK: No official tokenizer is available for Groq + return tiktoken.encoding_for_model("gpt-3.5-turbo") diff --git a/autogpts/forge/forge/llm/providers/multi.py b/forge/forge/llm/providers/multi.py similarity index 71% rename from autogpts/forge/forge/llm/providers/multi.py rename to forge/forge/llm/providers/multi.py index eadb9c13bf..93784cd2ea 100644 --- a/autogpts/forge/forge/llm/providers/multi.py +++ b/forge/forge/llm/providers/multi.py @@ -1,19 +1,18 @@ from __future__ import annotations import logging -from typing import Callable, Iterator, Optional, TypeVar +from typing import Any, Callable, Iterator, Optional, Sequence, TypeVar from pydantic import ValidationError -from forge.models.config import Configurable - from .anthropic import ANTHROPIC_CHAT_MODELS, AnthropicModelName, AnthropicProvider +from .groq import GROQ_CHAT_MODELS, GroqModelName, GroqProvider from .openai import OPEN_AI_CHAT_MODELS, OpenAIModelName, OpenAIProvider from .schema import ( AssistantChatMessage, + BaseChatModelProvider, ChatMessage, ChatModelInfo, - ChatModelProvider, ChatModelResponse, CompletionModelFunction, ModelProviderBudget, @@ -25,12 +24,13 @@ from .schema import ( _T = TypeVar("_T") -ModelName = AnthropicModelName | OpenAIModelName +ModelName = AnthropicModelName | GroqModelName | OpenAIModelName +EmbeddingModelProvider = OpenAIProvider -CHAT_MODELS = {**ANTHROPIC_CHAT_MODELS, **OPEN_AI_CHAT_MODELS} +CHAT_MODELS = {**ANTHROPIC_CHAT_MODELS, **GROQ_CHAT_MODELS, **OPEN_AI_CHAT_MODELS} -class MultiProvider(Configurable[ModelProviderSettings], ChatModelProvider): +class MultiProvider(BaseChatModelProvider[ModelName, ModelProviderSettings]): default_settings = ModelProviderSettings( name="multi_provider", description=( @@ -56,32 +56,37 @@ class MultiProvider(Configurable[ModelProviderSettings], ChatModelProvider): self._provider_instances = {} - async def get_available_models(self) -> list[ChatModelInfo]: + async def get_available_models(self) -> Sequence[ChatModelInfo[ModelName]]: + # TODO: support embeddings + return await self.get_available_chat_models() + + async def get_available_chat_models(self) -> Sequence[ChatModelInfo[ModelName]]: models = [] for provider in self.get_available_providers(): - models.extend(await provider.get_available_models()) + models.extend(await provider.get_available_chat_models()) return models def get_token_limit(self, model_name: ModelName) -> int: """Get the token limit for a given model.""" - return self.get_model_provider(model_name).get_token_limit(model_name) - - @classmethod - def get_tokenizer(cls, model_name: ModelName) -> ModelTokenizer: - return cls._get_model_provider_class(model_name).get_tokenizer(model_name) - - @classmethod - def count_tokens(cls, text: str, model_name: ModelName) -> int: - return cls._get_model_provider_class(model_name).count_tokens( - text=text, model_name=model_name + return self.get_model_provider(model_name).get_token_limit( + model_name # type: ignore + ) + + def get_tokenizer(self, model_name: ModelName) -> ModelTokenizer[Any]: + return self.get_model_provider(model_name).get_tokenizer( + model_name # type: ignore + ) + + def count_tokens(self, text: str, model_name: ModelName) -> int: + return self.get_model_provider(model_name).count_tokens( + text=text, model_name=model_name # type: ignore ) - @classmethod def count_message_tokens( - cls, messages: ChatMessage | list[ChatMessage], model_name: ModelName + self, messages: ChatMessage | list[ChatMessage], model_name: ModelName ) -> int: - return cls._get_model_provider_class(model_name).count_message_tokens( - messages=messages, model_name=model_name + return self.get_model_provider(model_name).count_message_tokens( + messages=messages, model_name=model_name # type: ignore ) async def create_chat_completion( @@ -97,7 +102,7 @@ class MultiProvider(Configurable[ModelProviderSettings], ChatModelProvider): """Create a completion using the Anthropic API.""" return await self.get_model_provider(model_name).create_chat_completion( model_prompt=model_prompt, - model_name=model_name, + model_name=model_name, # type: ignore completion_parser=completion_parser, functions=functions, max_output_tokens=max_output_tokens, @@ -135,24 +140,19 @@ class MultiProvider(Configurable[ModelProviderSettings], ChatModelProvider): ) from e self._provider_instances[provider_name] = _provider = Provider( - settings=settings, logger=self._logger + settings=settings, logger=self._logger # type: ignore ) _provider._budget = self._budget # Object binding not preserved by Pydantic return _provider - @classmethod - def _get_model_provider_class( - cls, model_name: ModelName - ) -> type[AnthropicProvider | OpenAIProvider]: - return cls._get_provider_class(CHAT_MODELS[model_name].provider_name) - @classmethod def _get_provider_class( cls, provider_name: ModelProviderName - ) -> type[AnthropicProvider | OpenAIProvider]: + ) -> type[AnthropicProvider | GroqProvider | OpenAIProvider]: try: return { ModelProviderName.ANTHROPIC: AnthropicProvider, + ModelProviderName.GROQ: GroqProvider, ModelProviderName.OPENAI: OpenAIProvider, }[provider_name] except KeyError: @@ -160,3 +160,6 @@ class MultiProvider(Configurable[ModelProviderSettings], ChatModelProvider): def __repr__(self): return f"{self.__class__.__name__}()" + + +ChatModelProvider = AnthropicProvider | GroqProvider | OpenAIProvider | MultiProvider diff --git a/autogpts/forge/forge/llm/providers/openai.py b/forge/forge/llm/providers/openai.py similarity index 55% rename from autogpts/forge/forge/llm/providers/openai.py rename to forge/forge/llm/providers/openai.py index aae5c1a42a..86473bec4c 100644 --- a/autogpts/forge/forge/llm/providers/openai.py +++ b/forge/forge/llm/providers/openai.py @@ -3,36 +3,33 @@ import inspect import logging import os from pathlib import Path -from typing import Any, Callable, Coroutine, Iterator, Optional, ParamSpec, TypeVar +from typing import Any, Callable, Iterator, Mapping, Optional, ParamSpec, TypeVar, cast -import sentry_sdk import tenacity import tiktoken import yaml from openai._exceptions import APIStatusError, RateLimitError -from openai.types import CreateEmbeddingResponse +from openai.types import EmbeddingCreateParams from openai.types.chat import ( - ChatCompletion, ChatCompletionMessage, ChatCompletionMessageParam, + CompletionCreateParams, ) from pydantic import SecretStr from forge.json.parsing import json_loads -from forge.llm.providers.schema import ( - AssistantChatMessage, - AssistantFunctionCall, +from forge.models.config import UserConfigurable +from forge.models.json_schema import JSONSchema + +from ._openai_base import BaseOpenAIChatProvider, BaseOpenAIEmbeddingProvider +from .schema import ( AssistantToolCall, AssistantToolCallDict, ChatMessage, ChatModelInfo, - ChatModelProvider, - ChatModelResponse, CompletionModelFunction, Embedding, EmbeddingModelInfo, - EmbeddingModelProvider, - EmbeddingModelResponse, ModelProviderBudget, ModelProviderConfiguration, ModelProviderCredentials, @@ -40,10 +37,6 @@ from forge.llm.providers.schema import ( ModelProviderSettings, ModelTokenizer, ) -from forge.models.config import Configurable, UserConfigurable -from forge.models.json_schema import JSONSchema - -from .utils import validate_tool_calls _T = TypeVar("_T") _P = ParamSpec("_P") @@ -79,8 +72,11 @@ class OpenAIModelName(str, enum.Enum): GPT4_TURBO = "gpt-4-turbo" GPT4_TURBO_PREVIEW = "gpt-4-turbo-preview" GPT4_VISION = "gpt-4-vision-preview" + GPT4_O_v1 = "gpt-4o-2024-05-13" + GPT4_O_ROLLING = "gpt-4o" GPT4 = GPT4_ROLLING GPT4_32k = GPT4_ROLLING_32k + GPT4_O = GPT4_O_ROLLING OPEN_AI_EMBEDDING_MODELS = { @@ -170,6 +166,14 @@ OPEN_AI_CHAT_MODELS = { max_tokens=128000, has_function_call_api=True, ), + ChatModelInfo( + name=OpenAIModelName.GPT4_O, + provider_name=ModelProviderName.OPENAI, + prompt_token_cost=5 / 1_000_000, + completion_token_cost=15 / 1_000_000, + max_tokens=128_000, + has_function_call_api=True, + ), ] } # Copy entries for models with equivalent specs @@ -190,6 +194,7 @@ chat_model_mapping = { OpenAIModelName.GPT4_TURBO_PREVIEW, OpenAIModelName.GPT4_v5, ], + OpenAIModelName.GPT4_O: [OpenAIModelName.GPT4_O_v1], } for base, copies in chat_model_mapping.items(): for copy in copies: @@ -199,56 +204,58 @@ for base, copies in chat_model_mapping.items(): copy_info.has_function_call_api = False -OPEN_AI_MODELS = { +OPEN_AI_MODELS: Mapping[ + OpenAIModelName, + ChatModelInfo[OpenAIModelName] | EmbeddingModelInfo[OpenAIModelName], +] = { **OPEN_AI_CHAT_MODELS, **OPEN_AI_EMBEDDING_MODELS, } -class OpenAIConfiguration(ModelProviderConfiguration): - fix_failed_parse_tries: int = UserConfigurable(3) - - class OpenAICredentials(ModelProviderCredentials): """Credentials for OpenAI.""" - api_key: SecretStr = UserConfigurable(from_env="OPENAI_API_KEY") + api_key: SecretStr = UserConfigurable(from_env="OPENAI_API_KEY") # type: ignore api_base: Optional[SecretStr] = UserConfigurable( default=None, from_env="OPENAI_API_BASE_URL" ) organization: Optional[SecretStr] = UserConfigurable(from_env="OPENAI_ORGANIZATION") - api_type: str = UserConfigurable( - default="", - from_env=lambda: ( + api_type: Optional[SecretStr] = UserConfigurable( + default=None, + from_env=lambda: cast( + SecretStr | None, "azure" if os.getenv("USE_AZURE") == "True" - else os.getenv("OPENAI_API_TYPE") + else os.getenv("OPENAI_API_TYPE"), ), ) - api_version: str = UserConfigurable("", from_env="OPENAI_API_VERSION") + api_version: Optional[SecretStr] = UserConfigurable( + default=None, from_env="OPENAI_API_VERSION" + ) azure_endpoint: Optional[SecretStr] = None azure_model_to_deploy_id_map: Optional[dict[str, str]] = None def get_api_access_kwargs(self) -> dict[str, str]: kwargs = { - k: (v.get_secret_value() if type(v) is SecretStr else v) + k: v.get_secret_value() for k, v in { "api_key": self.api_key, "base_url": self.api_base, "organization": self.organization, + "api_version": self.api_version, }.items() if v is not None } - if self.api_type == "azure": - kwargs["api_version"] = self.api_version + if self.api_type == SecretStr("azure"): assert self.azure_endpoint, "Azure endpoint not configured" kwargs["azure_endpoint"] = self.azure_endpoint.get_secret_value() return kwargs def get_model_access_kwargs(self, model: str) -> dict[str, str]: kwargs = {"model": model} - if self.api_type == "azure" and model: + if self.api_type == SecretStr("azure") and model: azure_kwargs = self._get_azure_access_kwargs(model) kwargs.update(azure_kwargs) return kwargs @@ -265,7 +272,7 @@ class OpenAICredentials(ModelProviderCredentials): raise ValueError(*e.args) self.api_type = config_params.get("azure_api_type", "azure") - self.api_version = config_params.get("azure_api_version", "") + self.api_version = config_params.get("azure_api_version", None) self.azure_endpoint = config_params.get("azure_endpoint") self.azure_model_to_deploy_id_map = config_params.get("azure_model_map") @@ -283,26 +290,28 @@ class OpenAICredentials(ModelProviderCredentials): class OpenAISettings(ModelProviderSettings): - configuration: OpenAIConfiguration - credentials: Optional[OpenAICredentials] - budget: ModelProviderBudget + credentials: Optional[OpenAICredentials] # type: ignore + budget: ModelProviderBudget # type: ignore class OpenAIProvider( - Configurable[OpenAISettings], ChatModelProvider, EmbeddingModelProvider + BaseOpenAIChatProvider[OpenAIModelName, OpenAISettings], + BaseOpenAIEmbeddingProvider[OpenAIModelName, OpenAISettings], ): + MODELS = OPEN_AI_MODELS + CHAT_MODELS = OPEN_AI_CHAT_MODELS + EMBEDDING_MODELS = OPEN_AI_EMBEDDING_MODELS + default_settings = OpenAISettings( name="openai_provider", description="Provides access to OpenAI's API.", - configuration=OpenAIConfiguration( - retries_per_request=7, - ), + configuration=ModelProviderConfiguration(), credentials=None, budget=ModelProviderBudget(), ) _settings: OpenAISettings - _configuration: OpenAIConfiguration + _configuration: ModelProviderConfiguration _credentials: OpenAICredentials _budget: ModelProviderBudget @@ -311,44 +320,28 @@ class OpenAIProvider( settings: Optional[OpenAISettings] = None, logger: Optional[logging.Logger] = None, ): - if not settings: - settings = self.default_settings.copy(deep=True) - if not settings.credentials: - settings.credentials = OpenAICredentials.from_env() - super(OpenAIProvider, self).__init__(settings=settings, logger=logger) - if self._credentials.api_type == "azure": + if self._credentials.api_type == SecretStr("azure"): from openai import AsyncAzureOpenAI # API key and org (if configured) are passed, the rest of the required # credentials is loaded from the environment by the AzureOpenAI client. - self._client = AsyncAzureOpenAI(**self._credentials.get_api_access_kwargs()) + self._client = AsyncAzureOpenAI( + **self._credentials.get_api_access_kwargs() # type: ignore + ) else: from openai import AsyncOpenAI - self._client = AsyncOpenAI(**self._credentials.get_api_access_kwargs()) + self._client = AsyncOpenAI( + **self._credentials.get_api_access_kwargs() # type: ignore + ) - async def get_available_models(self) -> list[ChatModelInfo]: - _models = (await self._client.models.list()).data - return [OPEN_AI_MODELS[m.id] for m in _models if m.id in OPEN_AI_MODELS] - - def get_token_limit(self, model_name: str) -> int: - """Get the token limit for a given model.""" - return OPEN_AI_MODELS[model_name].max_tokens - - @classmethod - def get_tokenizer(cls, model_name: OpenAIModelName) -> ModelTokenizer: + def get_tokenizer(self, model_name: OpenAIModelName) -> ModelTokenizer[int]: return tiktoken.encoding_for_model(model_name) - @classmethod - def count_tokens(cls, text: str, model_name: OpenAIModelName) -> int: - encoding = cls.get_tokenizer(model_name) - return len(encoding.encode(text)) - - @classmethod def count_message_tokens( - cls, + self, messages: ChatMessage | list[ChatMessage], model_name: OpenAIModelName, ) -> int: @@ -360,328 +353,87 @@ class OpenAIProvider( 4 # every message follows <|start|>{role/name}\n{content}<|end|>\n ) tokens_per_name = -1 # if there's a name, the role is omitted - encoding_model = "gpt-3.5-turbo" + # TODO: check if this is still valid for gpt-4o elif model_name.startswith("gpt-4"): tokens_per_message = 3 tokens_per_name = 1 - encoding_model = "gpt-4" else: raise NotImplementedError( f"count_message_tokens() is not implemented for model {model_name}.\n" - " See https://github.com/openai/openai-python/blob/main/chatml.md for" - " information on how messages are converted to tokens." + "See https://github.com/openai/openai-python/blob/120d225b91a8453e15240a49fb1c6794d8119326/chatml.md " # noqa + "for information on how messages are converted to tokens." ) - try: - encoding = tiktoken.encoding_for_model(encoding_model) - except KeyError: - logging.getLogger(__class__.__name__).warning( - f"Model {model_name} not found. Defaulting to cl100k_base encoding." - ) - encoding = tiktoken.get_encoding("cl100k_base") + tokenizer = self.get_tokenizer(model_name) num_tokens = 0 for message in messages: num_tokens += tokens_per_message for key, value in message.dict().items(): - num_tokens += len(encoding.encode(value)) + num_tokens += len(tokenizer.encode(value)) if key == "name": num_tokens += tokens_per_name num_tokens += 3 # every reply is primed with <|start|>assistant<|message|> return num_tokens - async def create_chat_completion( - self, - model_prompt: list[ChatMessage], - model_name: OpenAIModelName, - completion_parser: Callable[[AssistantChatMessage], _T] = lambda _: None, - functions: Optional[list[CompletionModelFunction]] = None, - max_output_tokens: Optional[int] = None, - prefill_response: str = "", # not supported by OpenAI - **kwargs, - ) -> ChatModelResponse[_T]: - """Create a completion using the OpenAI API and parse it.""" - - openai_messages, completion_kwargs = self._get_chat_completion_args( - model_prompt=model_prompt, - model_name=model_name, - functions=functions, - max_tokens=max_output_tokens, - **kwargs, - ) - tool_calls_compat_mode = bool(functions and "tools" not in completion_kwargs) - - total_cost = 0.0 - attempts = 0 - while True: - _response, _cost, t_input, t_output = await self._create_chat_completion( - messages=openai_messages, - **completion_kwargs, - ) - total_cost += _cost - - # If parsing the response fails, append the error to the prompt, and let the - # LLM fix its mistake(s). - attempts += 1 - parse_errors: list[Exception] = [] - - _assistant_msg = _response.choices[0].message - - tool_calls, _errors = self._parse_assistant_tool_calls( - _assistant_msg, tool_calls_compat_mode - ) - parse_errors += _errors - - # Validate tool calls - if not parse_errors and tool_calls and functions: - parse_errors += validate_tool_calls(tool_calls, functions) - - assistant_msg = AssistantChatMessage( - content=_assistant_msg.content, - tool_calls=tool_calls or None, - ) - - parsed_result: _T = None # type: ignore - if not parse_errors: - try: - parsed_result = completion_parser(assistant_msg) - if inspect.isawaitable(parsed_result): - parsed_result = await parsed_result - except Exception as e: - parse_errors.append(e) - - if not parse_errors: - if attempts > 1: - self._logger.debug( - f"Total cost for {attempts} attempts: ${round(total_cost, 5)}" - ) - - return ChatModelResponse( - response=AssistantChatMessage( - content=_assistant_msg.content, - tool_calls=tool_calls or None, - ), - parsed_result=parsed_result, - model_info=OPEN_AI_CHAT_MODELS[model_name], - prompt_tokens_used=t_input, - completion_tokens_used=t_output, - ) - - else: - self._logger.debug( - f"Parsing failed on response: '''{_assistant_msg}'''" - ) - parse_errors_fmt = "\n\n".join( - f"{e.__class__.__name__}: {e}" for e in parse_errors - ) - self._logger.warning( - f"Parsing attempt #{attempts} failed: {parse_errors_fmt}" - ) - for e in parse_errors: - sentry_sdk.capture_exception( - error=e, - extras={"assistant_msg": _assistant_msg, "i_attempt": attempts}, - ) - - if attempts < self._configuration.fix_failed_parse_tries: - openai_messages.append(_assistant_msg.dict(exclude_none=True)) - openai_messages.append( - { - "role": "system", - "content": ( - f"ERROR PARSING YOUR RESPONSE:\n\n{parse_errors_fmt}" - ), - } - ) - continue - else: - raise parse_errors[0] - - async def create_embedding( - self, - text: str, - model_name: OpenAIModelName, - embedding_parser: Callable[[Embedding], Embedding], - **kwargs, - ) -> EmbeddingModelResponse: - """Create an embedding using the OpenAI API.""" - embedding_kwargs = self._get_embedding_kwargs(model_name, **kwargs) - response = await self._create_embedding(text=text, **embedding_kwargs) - - response = EmbeddingModelResponse( - embedding=embedding_parser(response.data[0].embedding), - model_info=OPEN_AI_EMBEDDING_MODELS[model_name], - prompt_tokens_used=response.usage.prompt_tokens, - completion_tokens_used=0, - ) - self._budget.update_usage_and_cost(response) - return response - def _get_chat_completion_args( self, - model_prompt: list[ChatMessage], - model_name: OpenAIModelName, + prompt_messages: list[ChatMessage], + model: OpenAIModelName, functions: Optional[list[CompletionModelFunction]] = None, + max_output_tokens: Optional[int] = None, **kwargs, - ) -> tuple[list[ChatCompletionMessageParam], dict[str, Any]]: - """Prepare chat completion arguments and keyword arguments for API call. + ) -> tuple[ + list[ChatCompletionMessageParam], CompletionCreateParams, dict[str, Any] + ]: + """Prepare keyword arguments for an OpenAI chat completion call Args: - model_prompt: List of ChatMessages. - model_name: The model to use. - functions: Optional list of functions available to the LLM. - kwargs: Additional keyword arguments. + prompt_messages: List of ChatMessages + model: The model to use + functions (optional): List of functions available to the LLM + max_output_tokens (optional): Maximum number of tokens to generate Returns: list[ChatCompletionMessageParam]: Prompt messages for the OpenAI call - dict[str, Any]: Any other kwargs for the OpenAI call + CompletionCreateParams: Mapping of other kwargs for the OpenAI call + Mapping[str, Any]: Any keyword arguments to pass on to the completion parser """ - kwargs.update(self._credentials.get_model_access_kwargs(model_name)) - + tools_compat_mode = False if functions: - if OPEN_AI_CHAT_MODELS[model_name].has_function_call_api: - kwargs["tools"] = [ - {"type": "function", "function": f.schema} for f in functions - ] - if len(functions) == 1: - # force the model to call the only specified function - kwargs["tool_choice"] = { - "type": "function", - "function": {"name": functions[0].name}, - } - else: + if not OPEN_AI_CHAT_MODELS[model].has_function_call_api: # Provide compatibility with older models - _functions_compat_fix_kwargs(functions, kwargs) + _functions_compat_fix_kwargs(functions, prompt_messages) + tools_compat_mode = True + functions = None - if extra_headers := self._configuration.extra_request_headers: - kwargs["extra_headers"] = kwargs.get("extra_headers", {}) - kwargs["extra_headers"].update(extra_headers.copy()) - - if "messages" in kwargs: - model_prompt += kwargs["messages"] - del kwargs["messages"] - - openai_messages: list[ChatCompletionMessageParam] = [ - message.dict( - include={"role", "content", "tool_calls", "name"}, - exclude_none=True, - ) - for message in model_prompt - ] - - return openai_messages, kwargs - - def _get_embedding_kwargs( - self, - model_name: OpenAIModelName, - **kwargs, - ) -> dict: - """Get kwargs for embedding API call. - - Args: - model: The model to use. - kwargs: Keyword arguments to override the default values. - - Returns: - The kwargs for the embedding API call. - - """ - kwargs.update(self._credentials.get_model_access_kwargs(model_name)) - - if extra_headers := self._configuration.extra_request_headers: - kwargs["extra_headers"] = kwargs.get("extra_headers", {}) - kwargs["extra_headers"].update(extra_headers.copy()) - - return kwargs - - async def _create_chat_completion( - self, - messages: list[ChatCompletionMessageParam], - model: OpenAIModelName, - *_, - **kwargs, - ) -> tuple[ChatCompletion, float, int, int]: - """ - Create a chat completion using the OpenAI API with retry handling. - - Params: - openai_messages: List of OpenAI-consumable message dict objects - model: The model to use for the completion - - Returns: - ChatCompletion: The chat completion response object - float: The cost ($) of this completion - int: Number of prompt tokens used - int: Number of completion tokens used - """ - - @self._retry_api_request - async def _create_chat_completion_with_retry( - messages: list[ChatCompletionMessageParam], **kwargs - ) -> ChatCompletion: - return await self._client.chat.completions.create( - messages=messages, # type: ignore - **kwargs, - ) - - completion = await _create_chat_completion_with_retry( - messages, model=model, **kwargs + openai_messages, kwargs, parse_kwargs = super()._get_chat_completion_args( + prompt_messages=prompt_messages, + model=model, + functions=functions, + max_output_tokens=max_output_tokens, + **kwargs, ) + kwargs.update(self._credentials.get_model_access_kwargs(model)) # type: ignore - if completion.usage: - prompt_tokens_used = completion.usage.prompt_tokens - completion_tokens_used = completion.usage.completion_tokens - else: - prompt_tokens_used = completion_tokens_used = 0 + if tools_compat_mode: + parse_kwargs["compat_mode"] = True - cost = self._budget.update_usage_and_cost( - model_info=OPEN_AI_CHAT_MODELS[model], - input_tokens_used=prompt_tokens_used, - output_tokens_used=completion_tokens_used, - ) - self._logger.debug( - f"Completion usage: {prompt_tokens_used} input, " - f"{completion_tokens_used} output - ${round(cost, 5)}" - ) - return completion, cost, prompt_tokens_used, completion_tokens_used + return openai_messages, kwargs, parse_kwargs def _parse_assistant_tool_calls( - self, assistant_message: ChatCompletionMessage, compat_mode: bool = False - ): + self, + assistant_message: ChatCompletionMessage, + compat_mode: bool = False, + **kwargs, + ) -> tuple[list[AssistantToolCall], list[Exception]]: tool_calls: list[AssistantToolCall] = [] parse_errors: list[Exception] = [] - if assistant_message.tool_calls: - for _tc in assistant_message.tool_calls: - try: - parsed_arguments = json_loads(_tc.function.arguments) - except Exception as e: - err_message = ( - f"Decoding arguments for {_tc.function.name} failed: " - + str(e.args[0]) - ) - parse_errors.append( - type(e)(err_message, *e.args[1:]).with_traceback( - e.__traceback__ - ) - ) - continue - - tool_calls.append( - AssistantToolCall( - id=_tc.id, - type=_tc.type, - function=AssistantFunctionCall( - name=_tc.function.name, - arguments=parsed_arguments, - ), - ) - ) - - # If parsing of all tool calls succeeds in the end, we ignore any issues - if len(tool_calls) == len(assistant_message.tool_calls): - parse_errors = [] - - elif compat_mode and assistant_message.content: + if not compat_mode: + return super()._parse_assistant_tool_calls( + assistant_message=assistant_message, compat_mode=compat_mode, **kwargs + ) + elif assistant_message.content: try: tool_calls = list( _tool_calls_compat_extract_calls(assistant_message.content) @@ -691,21 +443,16 @@ class OpenAIProvider( return tool_calls, parse_errors - def _create_embedding( - self, text: str, *_, **kwargs - ) -> Coroutine[None, None, CreateEmbeddingResponse]: - """Create an embedding using the OpenAI API with retry handling.""" + def _get_embedding_kwargs( + self, input: str | list[str], model: OpenAIModelName, **kwargs + ) -> EmbeddingCreateParams: + kwargs = super()._get_embedding_kwargs(input=input, model=model, **kwargs) + kwargs.update(self._credentials.get_model_access_kwargs(model)) # type: ignore + return kwargs - @self._retry_api_request - async def _create_embedding_with_retry( - text: str, *_, **kwargs - ) -> CreateEmbeddingResponse: - return await self._client.embeddings.create( - input=[text], - **kwargs, - ) - - return _create_embedding_with_retry(text, *_, **kwargs) + _get_embedding_kwargs.__doc__ = ( + BaseOpenAIEmbeddingProvider._get_embedding_kwargs.__doc__ + ) def _retry_api_request(self, func: Callable[_P, _T]) -> Callable[_P, _T]: _log_retry_debug_message = tenacity.after_log(self._logger, logging.DEBUG) @@ -816,7 +563,7 @@ def count_openai_functions_tokens( def _functions_compat_fix_kwargs( functions: list[CompletionModelFunction], - completion_kwargs: dict, + prompt_messages: list[ChatMessage], ): function_definitions = format_function_specs_as_typescript_ns(functions) function_call_schema = JSONSchema( @@ -847,7 +594,7 @@ def _functions_compat_fix_kwargs( }, ), ) - completion_kwargs["messages"] = [ + prompt_messages.append( ChatMessage.system( "# tool usage instructions\n\n" "Specify a '```tool_calls' block in your response," @@ -860,7 +607,7 @@ def _functions_compat_fix_kwargs( "For the function call itself, use one of the following" f" functions:\n\n{function_definitions}" ), - ] + ) def _tool_calls_compat_extract_calls(response: str) -> Iterator[AssistantToolCall]: @@ -879,6 +626,4 @@ def _tool_calls_compat_extract_calls(response: str) -> Iterator[AssistantToolCal for t in tool_calls: t["id"] = str(uuid.uuid4()) - t["function"]["arguments"] = str(t["function"]["arguments"]) # HACK - yield AssistantToolCall.parse_obj(t) diff --git a/autogpts/forge/forge/llm/providers/schema.py b/forge/forge/llm/providers/schema.py similarity index 74% rename from autogpts/forge/forge/llm/providers/schema.py rename to forge/forge/llm/providers/schema.py index 5a12092c3d..d7a432674a 100644 --- a/autogpts/forge/forge/llm/providers/schema.py +++ b/forge/forge/llm/providers/schema.py @@ -12,21 +12,25 @@ from typing import ( Literal, Optional, Protocol, + Sequence, TypedDict, TypeVar, ) -from pydantic import BaseModel, Field, SecretStr, validator +from pydantic import BaseModel, Field, SecretStr from forge.logging.utils import fmt_kwargs -from forge.models.config import SystemConfiguration, UserConfigurable +from forge.models.config import ( + Configurable, + SystemConfiguration, + SystemSettings, + UserConfigurable, +) from forge.models.json_schema import JSONSchema from forge.models.providers import ( Embedding, ProviderBudget, ProviderCredentials, - ProviderSettings, - ProviderUsage, ResourceType, ) @@ -34,6 +38,11 @@ if TYPE_CHECKING: from jsonschema import ValidationError +_T = TypeVar("_T") + +_ModelName = TypeVar("_ModelName", bound=str) + + class ModelProviderService(str, enum.Enum): """A ModelService describes what kind of service the model provides.""" @@ -45,6 +54,7 @@ class ModelProviderService(str, enum.Enum): class ModelProviderName(str, enum.Enum): OPENAI = "openai" ANTHROPIC = "anthropic" + GROQ = "groq" class ChatMessage(BaseModel): @@ -101,13 +111,13 @@ class AssistantToolCallDict(TypedDict): class AssistantChatMessage(ChatMessage): - role: Literal[ChatMessage.Role.ASSISTANT] = ChatMessage.Role.ASSISTANT - content: Optional[str] + role: Literal[ChatMessage.Role.ASSISTANT] = ChatMessage.Role.ASSISTANT # type: ignore # noqa + content: str = "" tool_calls: Optional[list[AssistantToolCall]] = None class ToolResultMessage(ChatMessage): - role: Literal[ChatMessage.Role.TOOL] = ChatMessage.Role.TOOL + role: Literal[ChatMessage.Role.TOOL] = ChatMessage.Role.TOOL # type: ignore is_error: bool = False tool_call_id: str @@ -127,32 +137,6 @@ class CompletionModelFunction(BaseModel): return_type: str | None = None is_async: bool = False - @property - def schema(self) -> dict[str, str | dict | list]: - """Returns an OpenAI-consumable function specification""" - - return { - "name": self.name, - "description": self.description, - "parameters": { - "type": "object", - "properties": { - name: param.to_dict() for name, param in self.parameters.items() - }, - "required": [ - name for name, param in self.parameters.items() if param.required - ], - }, - } - - @staticmethod - def parse(schema: dict) -> "CompletionModelFunction": - return CompletionModelFunction( - name=schema["name"], - description=schema["description"], - parameters=JSONSchema.parse_properties(schema["parameters"]), - ) - def fmt_line(self) -> str: params = ", ".join( f"{name}{'?' if not p.required else ''}: " f"{p.typescript_type}" @@ -219,15 +203,15 @@ class CompletionModelFunction(BaseModel): return params_schema.validate_object(function_call.arguments) -class ModelInfo(BaseModel): +class ModelInfo(BaseModel, Generic[_ModelName]): """Struct for model information. Would be lovely to eventually get this directly from APIs, but needs to be scraped from websites for now. """ - name: str - service: ModelProviderService + name: _ModelName + service: ClassVar[ModelProviderService] provider_name: ModelProviderName prompt_token_cost: float = 0.0 completion_token_cost: float = 0.0 @@ -242,7 +226,8 @@ class ModelResponse(BaseModel): class ModelProviderConfiguration(SystemConfiguration): - retries_per_request: int = UserConfigurable() + retries_per_request: int = UserConfigurable(7) + fix_failed_parse_tries: int = UserConfigurable(3) extra_request_headers: dict[str, str] = Field(default_factory=dict) @@ -255,27 +240,39 @@ class ModelProviderCredentials(ProviderCredentials): api_version: SecretStr | None = UserConfigurable(default=None) deployment_id: SecretStr | None = UserConfigurable(default=None) - class Config: + class Config(ProviderCredentials.Config): extra = "ignore" -class ModelProviderUsage(ProviderUsage): +class ModelProviderUsage(BaseModel): """Usage for a particular model from a model provider.""" - completion_tokens: int = 0 - prompt_tokens: int = 0 + class ModelUsage(BaseModel): + completion_tokens: int = 0 + prompt_tokens: int = 0 + + usage_per_model: dict[str, ModelUsage] = defaultdict(ModelUsage) + + @property + def completion_tokens(self) -> int: + return sum(model.completion_tokens for model in self.usage_per_model.values()) + + @property + def prompt_tokens(self) -> int: + return sum(model.prompt_tokens for model in self.usage_per_model.values()) def update_usage( self, + model: str, input_tokens_used: int, output_tokens_used: int = 0, ) -> None: - self.prompt_tokens += input_tokens_used - self.completion_tokens += output_tokens_used + self.usage_per_model[model].prompt_tokens += input_tokens_used + self.usage_per_model[model].completion_tokens += output_tokens_used -class ModelProviderBudget(ProviderBudget): - usage: defaultdict[str, ModelProviderUsage] = defaultdict(ModelProviderUsage) +class ModelProviderBudget(ProviderBudget[ModelProviderUsage]): + usage: ModelProviderUsage = Field(default_factory=ModelProviderUsage) def update_usage_and_cost( self, @@ -288,7 +285,7 @@ class ModelProviderBudget(ProviderBudget): Returns: float: The (calculated) cost of the given model response. """ - self.usage[model_info.name].update_usage(input_tokens_used, output_tokens_used) + self.usage.update_usage(model_info.name, input_tokens_used, output_tokens_used) incurred_cost = ( output_tokens_used * model_info.completion_token_cost + input_tokens_used * model_info.prompt_token_cost @@ -298,28 +295,33 @@ class ModelProviderBudget(ProviderBudget): return incurred_cost -class ModelProviderSettings(ProviderSettings): - resource_type: ResourceType = ResourceType.MODEL +class ModelProviderSettings(SystemSettings): + resource_type: ClassVar[ResourceType] = ResourceType.MODEL configuration: ModelProviderConfiguration credentials: Optional[ModelProviderCredentials] = None budget: Optional[ModelProviderBudget] = None -class ModelProvider(abc.ABC): +_ModelProviderSettings = TypeVar("_ModelProviderSettings", bound=ModelProviderSettings) + + +# TODO: either use MultiProvider throughout codebase as type for `llm_provider`, or +# replace `_ModelName` by `str` to eliminate type checking difficulties +class BaseModelProvider( + abc.ABC, + Generic[_ModelName, _ModelProviderSettings], + Configurable[_ModelProviderSettings], +): """A ModelProvider abstracts the details of a particular provider of models.""" - default_settings: ClassVar[ModelProviderSettings] - - _settings: ModelProviderSettings - _configuration: ModelProviderConfiguration - _credentials: Optional[ModelProviderCredentials] = None - _budget: Optional[ModelProviderBudget] = None + default_settings: ClassVar[_ModelProviderSettings] # type: ignore + _settings: _ModelProviderSettings _logger: logging.Logger def __init__( self, - settings: Optional[ModelProviderSettings] = None, + settings: Optional[_ModelProviderSettings] = None, logger: Optional[logging.Logger] = None, ): if not settings: @@ -333,15 +335,21 @@ class ModelProvider(abc.ABC): self._logger = logger or logging.getLogger(self.__module__) @abc.abstractmethod - def count_tokens(self, text: str, model_name: str) -> int: + async def get_available_models( + self, + ) -> Sequence["ChatModelInfo[_ModelName] | EmbeddingModelInfo[_ModelName]"]: ... @abc.abstractmethod - def get_tokenizer(self, model_name: str) -> "ModelTokenizer": + def count_tokens(self, text: str, model_name: _ModelName) -> int: ... @abc.abstractmethod - def get_token_limit(self, model_name: str) -> int: + def get_tokenizer(self, model_name: _ModelName) -> "ModelTokenizer[Any]": + ... + + @abc.abstractmethod + def get_token_limit(self, model_name: _ModelName) -> int: ... def get_incurred_cost(self) -> float: @@ -355,15 +363,15 @@ class ModelProvider(abc.ABC): return math.inf -class ModelTokenizer(Protocol): +class ModelTokenizer(Protocol, Generic[_T]): """A ModelTokenizer provides tokenization specific to a model.""" @abc.abstractmethod - def encode(self, text: str) -> list: + def encode(self, text: str) -> list[_T]: ... @abc.abstractmethod - def decode(self, tokens: list) -> str: + def decode(self, tokens: list[_T]) -> str: ... @@ -372,10 +380,10 @@ class ModelTokenizer(Protocol): #################### -class EmbeddingModelInfo(ModelInfo): +class EmbeddingModelInfo(ModelInfo[_ModelName]): """Struct for embedding model information.""" - service: Literal[ModelProviderService.EMBEDDING] = ModelProviderService.EMBEDDING + service: Literal[ModelProviderService.EMBEDDING] = ModelProviderService.EMBEDDING # type: ignore # noqa max_tokens: int embedding_dimensions: int @@ -384,21 +392,21 @@ class EmbeddingModelResponse(ModelResponse): """Standard response struct for a response from an embedding model.""" embedding: Embedding = Field(default_factory=list) - - @classmethod - @validator("completion_tokens_used") - def _verify_no_completion_tokens_used(cls, v): - if v > 0: - raise ValueError("Embeddings should not have completion tokens used.") - return v + completion_tokens_used: int = Field(default=0, const=True) -class EmbeddingModelProvider(ModelProvider): +class BaseEmbeddingModelProvider(BaseModelProvider[_ModelName, _ModelProviderSettings]): + @abc.abstractmethod + async def get_available_embedding_models( + self, + ) -> Sequence[EmbeddingModelInfo[_ModelName]]: + ... + @abc.abstractmethod async def create_embedding( self, text: str, - model_name: str, + model_name: _ModelName, embedding_parser: Callable[[Embedding], Embedding], **kwargs, ) -> EmbeddingModelResponse: @@ -410,34 +418,31 @@ class EmbeddingModelProvider(ModelProvider): ############### -class ChatModelInfo(ModelInfo): +class ChatModelInfo(ModelInfo[_ModelName]): """Struct for language model information.""" - service: Literal[ModelProviderService.CHAT] = ModelProviderService.CHAT + service: Literal[ModelProviderService.CHAT] = ModelProviderService.CHAT # type: ignore # noqa max_tokens: int has_function_call_api: bool = False -_T = TypeVar("_T") - - class ChatModelResponse(ModelResponse, Generic[_T]): """Standard response struct for a response from a language model.""" response: AssistantChatMessage - parsed_result: _T = None + parsed_result: _T -class ChatModelProvider(ModelProvider): +class BaseChatModelProvider(BaseModelProvider[_ModelName, _ModelProviderSettings]): @abc.abstractmethod - async def get_available_models(self) -> list[ChatModelInfo]: + async def get_available_chat_models(self) -> Sequence[ChatModelInfo[_ModelName]]: ... @abc.abstractmethod def count_message_tokens( self, messages: ChatMessage | list[ChatMessage], - model_name: str, + model_name: _ModelName, ) -> int: ... @@ -445,7 +450,7 @@ class ChatModelProvider(ModelProvider): async def create_chat_completion( self, model_prompt: list[ChatMessage], - model_name: str, + model_name: _ModelName, completion_parser: Callable[[AssistantChatMessage], _T] = lambda _: None, functions: Optional[list[CompletionModelFunction]] = None, max_output_tokens: Optional[int] = None, diff --git a/autogpts/forge/forge/llm/providers/utils.py b/forge/forge/llm/providers/utils.py similarity index 100% rename from autogpts/forge/forge/llm/providers/utils.py rename to forge/forge/llm/providers/utils.py diff --git a/autogpts/forge/forge/logging/__init__.py b/forge/forge/logging/__init__.py similarity index 75% rename from autogpts/forge/forge/logging/__init__.py rename to forge/forge/logging/__init__.py index 0348e043b7..cf327efde2 100644 --- a/autogpts/forge/forge/logging/__init__.py +++ b/forge/forge/logging/__init__.py @@ -1,11 +1,9 @@ from .config import configure_logging from .filters import BelowLevelFilter from .formatters import FancyConsoleFormatter -from .helpers import user_friendly_output __all__ = [ "configure_logging", "BelowLevelFilter", "FancyConsoleFormatter", - "user_friendly_output", ] diff --git a/autogpts/forge/forge/logging/config.py b/forge/forge/logging/config.py similarity index 87% rename from autogpts/forge/forge/logging/config.py rename to forge/forge/logging/config.py index 59d87fe5c5..0b849bf5e5 100644 --- a/autogpts/forge/forge/logging/config.py +++ b/forge/forge/logging/config.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from .filters import BelowLevelFilter from .formatters import ForgeFormatter, StructuredLoggingFormatter -from .handlers import TTSHandler, TypingConsoleHandler +from .handlers import TTSHandler LOG_DIR = Path(__file__).parent.parent.parent / "logs" LOG_FILE = "activity.log" @@ -64,7 +64,7 @@ class LoggingConfig(SystemConfiguration): log_dir: Path = LOG_DIR log_file_format: Optional[LogFormatName] = UserConfigurable( default=LogFormatName.SIMPLE, - from_env=lambda: os.getenv( + from_env=lambda: os.getenv( # type: ignore "LOG_FILE_FORMAT", os.getenv("LOG_FORMAT", "simple") ), ) @@ -153,22 +153,6 @@ def configure_logging( stderr.setFormatter(console_formatter) log_handlers += [stdout, stderr] - # Console output handler which simulates typing - typing_console_handler = TypingConsoleHandler(stream=sys.stdout) - typing_console_handler.setLevel(logging.INFO) - typing_console_handler.setFormatter(console_formatter) - - # User friendly output logger (text + speech) - user_friendly_output_logger = logging.getLogger(USER_FRIENDLY_OUTPUT_LOGGER) - user_friendly_output_logger.setLevel(logging.INFO) - user_friendly_output_logger.addHandler( - typing_console_handler if not config.plain_console_output else stdout - ) - if tts_config: - user_friendly_output_logger.addHandler(TTSHandler(tts_config)) - user_friendly_output_logger.addHandler(stderr) - user_friendly_output_logger.propagate = False - # File output handlers if config.log_file_format is not None: if config.level < logging.ERROR: @@ -184,7 +168,6 @@ def configure_logging( activity_log_handler.setLevel(config.level) activity_log_handler.setFormatter(file_output_formatter) log_handlers += [activity_log_handler] - user_friendly_output_logger.addHandler(activity_log_handler) # ERROR log file handler error_log_handler = logging.FileHandler( @@ -193,7 +176,6 @@ def configure_logging( error_log_handler.setLevel(logging.ERROR) error_log_handler.setFormatter(ForgeFormatter(DEBUG_LOG_FORMAT, no_color=True)) log_handlers += [error_log_handler] - user_friendly_output_logger.addHandler(error_log_handler) # Configure the root logger logging.basicConfig( diff --git a/autogpts/forge/forge/logging/filters.py b/forge/forge/logging/filters.py similarity index 100% rename from autogpts/forge/forge/logging/filters.py rename to forge/forge/logging/filters.py diff --git a/autogpts/forge/forge/logging/formatters.py b/forge/forge/logging/formatters.py similarity index 100% rename from autogpts/forge/forge/logging/formatters.py rename to forge/forge/logging/formatters.py diff --git a/autogpts/forge/forge/logging/handlers.py b/forge/forge/logging/handlers.py similarity index 53% rename from autogpts/forge/forge/logging/handlers.py rename to forge/forge/logging/handlers.py index bbc605087b..a27a8e4f94 100644 --- a/autogpts/forge/forge/logging/handlers.py +++ b/forge/forge/logging/handlers.py @@ -2,9 +2,6 @@ from __future__ import annotations import json import logging -import random -import re -import time from typing import TYPE_CHECKING from forge.logging.utils import remove_color_codes @@ -14,39 +11,6 @@ if TYPE_CHECKING: from forge.speech import TTSConfig -class TypingConsoleHandler(logging.StreamHandler): - """Output stream to console using simulated typing""" - - # Typing speed settings in WPS (Words Per Second) - MIN_WPS = 25 - MAX_WPS = 100 - - def emit(self, record: logging.LogRecord) -> None: - min_typing_interval = 1 / TypingConsoleHandler.MAX_WPS - max_typing_interval = 1 / TypingConsoleHandler.MIN_WPS - - msg = self.format(record) - try: - # Split without discarding whitespace - words = re.findall(r"\S+\s*", msg) - - for i, word in enumerate(words): - self.stream.write(word) - self.flush() - if i >= len(words) - 1: - self.stream.write(self.terminator) - self.flush() - break - - interval = random.uniform(min_typing_interval, max_typing_interval) - # type faster after each word - min_typing_interval = min_typing_interval * 0.95 - max_typing_interval = max_typing_interval * 0.95 - time.sleep(interval) - except Exception: - self.handleError(record) - - class TTSHandler(logging.Handler): """Output messages to the configured TTS engine (if any)""" diff --git a/forge/forge/logging/utils.py b/forge/forge/logging/utils.py new file mode 100644 index 0000000000..fe8fdc60eb --- /dev/null +++ b/forge/forge/logging/utils.py @@ -0,0 +1,33 @@ +import logging +import re +from typing import Any + +from colorama import Fore + + +def remove_color_codes(s: str) -> str: + return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", s) + + +def fmt_kwargs(kwargs: dict) -> str: + return ", ".join(f"{n}={repr(v)}" for n, v in kwargs.items()) + + +def print_attribute( + title: str, value: Any, title_color: str = Fore.GREEN, value_color: str = "" +) -> None: + logger = logging.getLogger() + logger.info( + str(value), + extra={ + "title": f"{title.rstrip(':')}:", + "title_color": title_color, + "color": value_color, + }, + ) + + +def speak(message: str, level: int = logging.INFO) -> None: + from .config import SPEECH_OUTPUT_LOGGER + + logging.getLogger(SPEECH_OUTPUT_LOGGER).log(level, message) diff --git a/autogpts/forge/forge/models/action.py b/forge/forge/models/action.py similarity index 92% rename from autogpts/forge/forge/models/action.py rename to forge/forge/models/action.py index 3eefbf6883..4acf7b97f8 100644 --- a/autogpts/forge/forge/models/action.py +++ b/forge/forge/models/action.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Literal, Optional +from typing import Any, Literal, Optional, TypeVar from pydantic import BaseModel @@ -11,7 +11,10 @@ from .utils import ModelWithSummary class ActionProposal(BaseModel): thoughts: str | ModelWithSummary - use_tool: AssistantFunctionCall = None + use_tool: AssistantFunctionCall + + +AnyProposal = TypeVar("AnyProposal", bound=ActionProposal) class ActionSuccessResult(BaseModel): diff --git a/autogpts/forge/forge/models/config.py b/forge/forge/models/config.py similarity index 99% rename from autogpts/forge/forge/models/config.py rename to forge/forge/models/config.py index 5bc95ffac2..5b83258f47 100644 --- a/autogpts/forge/forge/models/config.py +++ b/forge/forge/models/config.py @@ -1,4 +1,3 @@ -import abc import os import typing from typing import Any, Callable, Generic, Optional, Type, TypeVar, get_args @@ -85,11 +84,11 @@ class SystemSettings(BaseModel): S = TypeVar("S", bound=SystemSettings) -class Configurable(abc.ABC, Generic[S]): +class Configurable(Generic[S]): """A base class for all configurable objects.""" prefix: str = "" - default_settings: typing.ClassVar[S] + default_settings: typing.ClassVar[S] # type: ignore @classmethod def get_user_config(cls) -> dict[str, Any]: diff --git a/autogpts/forge/forge/models/json_schema.py b/forge/forge/models/json_schema.py similarity index 80% rename from autogpts/forge/forge/models/json_schema.py rename to forge/forge/models/json_schema.py index 60b508a129..04c0f83ff3 100644 --- a/autogpts/forge/forge/models/json_schema.py +++ b/forge/forge/models/json_schema.py @@ -1,7 +1,7 @@ import ast import enum from textwrap import indent -from typing import Any, Optional +from typing import Any, Optional, overload from jsonschema import Draft7Validator, ValidationError from pydantic import BaseModel @@ -61,30 +61,8 @@ class JSONSchema(BaseModel): @staticmethod def from_dict(schema: dict) -> "JSONSchema": - def resolve_references(schema: dict, definitions: dict) -> dict: - """ - Recursively resolve type $refs in the JSON schema with their definitions. - """ - if isinstance(schema, dict): - if "$ref" in schema: - ref_path = schema["$ref"].split("/")[ - 2: - ] # Split and remove '#/definitions' - ref_value = definitions - for key in ref_path: - ref_value = ref_value[key] - return resolve_references(ref_value, definitions) - else: - return { - k: resolve_references(v, definitions) for k, v in schema.items() - } - elif isinstance(schema, list): - return [resolve_references(item, definitions) for item in schema] - else: - return schema - definitions = schema.get("definitions", {}) - schema = resolve_references(schema, definitions) + schema = _resolve_type_refs_in_schema(schema, definitions) return JSONSchema( description=schema.get("description"), @@ -181,25 +159,59 @@ class JSONSchema(BaseModel): @property def typescript_type(self) -> str: + if not self.type: + return "any" if self.type == JSONSchema.Type.BOOLEAN: return "boolean" - elif self.type in {JSONSchema.Type.INTEGER, JSONSchema.Type.NUMBER}: + if self.type in {JSONSchema.Type.INTEGER, JSONSchema.Type.NUMBER}: return "number" - elif self.type == JSONSchema.Type.STRING: + if self.type == JSONSchema.Type.STRING: return "string" - elif self.type == JSONSchema.Type.ARRAY: + if self.type == JSONSchema.Type.ARRAY: return f"Array<{self.items.typescript_type}>" if self.items else "Array" - elif self.type == JSONSchema.Type.OBJECT: + if self.type == JSONSchema.Type.OBJECT: if not self.properties: return "Record" return self.to_typescript_object_interface() - elif self.enum: + if self.enum: return " | ".join(repr(v) for v in self.enum) elif self.type == JSONSchema.Type.TYPE: return "type" elif self.type is None: return "any" + + raise NotImplementedError( + f"JSONSchema.typescript_type does not support Type.{self.type.name} yet" + ) + + +@overload +def _resolve_type_refs_in_schema(schema: dict, definitions: dict) -> dict: + ... + + +@overload +def _resolve_type_refs_in_schema(schema: list, definitions: dict) -> list: + ... + + +def _resolve_type_refs_in_schema(schema: dict | list, definitions: dict) -> dict | list: + """ + Recursively resolve type $refs in the JSON schema with their definitions. + """ + if isinstance(schema, dict): + if "$ref" in schema: + ref_path = schema["$ref"].split("/")[2:] # Split and remove '#/definitions' + ref_value = definitions + for key in ref_path: + ref_value = ref_value[key] + return _resolve_type_refs_in_schema(ref_value, definitions) else: - raise NotImplementedError( - f"JSONSchema.typescript_type does not support Type.{self.type.name} yet" - ) + return { + k: _resolve_type_refs_in_schema(v, definitions) + for k, v in schema.items() + } + elif isinstance(schema, list): + return [_resolve_type_refs_in_schema(item, definitions) for item in schema] + else: + return schema diff --git a/autogpts/forge/forge/models/providers.py b/forge/forge/models/providers.py similarity index 70% rename from autogpts/forge/forge/models/providers.py rename to forge/forge/models/providers.py index d422ff46f0..19536e2acd 100644 --- a/autogpts/forge/forge/models/providers.py +++ b/forge/forge/models/providers.py @@ -1,31 +1,26 @@ import abc import enum import math +from typing import Callable, Generic, TypeVar from pydantic import BaseModel, SecretBytes, SecretField, SecretStr -from forge.models.config import SystemConfiguration, SystemSettings, UserConfigurable +from forge.models.config import SystemConfiguration, UserConfigurable + +_T = TypeVar("_T") class ResourceType(str, enum.Enum): """An enumeration of resource types.""" MODEL = "model" - MEMORY = "memory" -class ProviderUsage(SystemConfiguration, abc.ABC): - @abc.abstractmethod - def update_usage(self, *args, **kwargs) -> None: - """Update the usage of the resource.""" - ... - - -class ProviderBudget(SystemConfiguration): +class ProviderBudget(SystemConfiguration, Generic[_T]): total_budget: float = UserConfigurable(math.inf) total_cost: float = 0 remaining_budget: float = math.inf - usage: ProviderUsage + usage: _T @abc.abstractmethod def update_usage_and_cost(self, *args, **kwargs) -> float: @@ -43,8 +38,8 @@ class ProviderCredentials(SystemConfiguration): def unmasked(self) -> dict: return unmask(self) - class Config: - json_encoders = { + class Config(SystemConfiguration.Config): + json_encoders: dict[type[SecretField], Callable[[SecretField], str | None]] = { SecretStr: lambda v: v.get_secret_value() if v else None, SecretBytes: lambda v: v.get_secret_value() if v else None, SecretField: lambda v: v.get_secret_value() if v else None, @@ -62,11 +57,5 @@ def unmask(model: BaseModel): return unmasked_fields -class ProviderSettings(SystemSettings): - resource_type: ResourceType - credentials: ProviderCredentials | None = None - budget: ProviderBudget | None = None - - # Used both by model providers and memory providers Embedding = list[float] diff --git a/autogpts/forge/forge/models/utils.py b/forge/forge/models/utils.py similarity index 100% rename from autogpts/forge/forge/models/utils.py rename to forge/forge/models/utils.py diff --git a/forge/forge/speech/__init__.py b/forge/forge/speech/__init__.py new file mode 100644 index 0000000000..3a150010fd --- /dev/null +++ b/forge/forge/speech/__init__.py @@ -0,0 +1,4 @@ +"""This module contains the (speech recognition and) speech synthesis functions.""" +from .say import TextToSpeechProvider, TTSConfig + +__all__ = ["TextToSpeechProvider", "TTSConfig"] diff --git a/autogpts/forge/forge/speech/base.py b/forge/forge/speech/base.py similarity index 95% rename from autogpts/forge/forge/speech/base.py rename to forge/forge/speech/base.py index fd9fda60fb..dee8251afa 100644 --- a/autogpts/forge/forge/speech/base.py +++ b/forge/forge/speech/base.py @@ -45,7 +45,7 @@ class VoiceBase: """ @abc.abstractmethod - def _speech(self, text: str, voice_index: int = 0) -> bool: + def _speech(self, text: str, voice_id: int = 0) -> bool: """ Play the given text. diff --git a/autogpts/forge/forge/speech/eleven_labs.py b/forge/forge/speech/eleven_labs.py similarity index 97% rename from autogpts/forge/forge/speech/eleven_labs.py rename to forge/forge/speech/eleven_labs.py index 253b13bbbb..afe99154f2 100644 --- a/autogpts/forge/forge/speech/eleven_labs.py +++ b/forge/forge/speech/eleven_labs.py @@ -66,7 +66,7 @@ class ElevenLabsSpeech(VoiceBase): if voice and voice not in PLACEHOLDERS: self._voices[voice_index] = voice - def _speech(self, text: str, voice_index: int = 0) -> bool: + def _speech(self, text: str, voice_id: int = 0) -> bool: """Speak text using elevenlabs.io's API Args: @@ -77,7 +77,7 @@ class ElevenLabsSpeech(VoiceBase): bool: True if the request was successful, False otherwise """ tts_url = ( - f"https://api.elevenlabs.io/v1/text-to-speech/{self._voices[voice_index]}" + f"https://api.elevenlabs.io/v1/text-to-speech/{self._voices[voice_id]}" ) response = requests.post(tts_url, headers=self._headers, json={"text": text}) diff --git a/autogpts/forge/forge/speech/gtts.py b/forge/forge/speech/gtts.py similarity index 87% rename from autogpts/forge/forge/speech/gtts.py rename to forge/forge/speech/gtts.py index 5a1f93675f..55c00cdae1 100644 --- a/autogpts/forge/forge/speech/gtts.py +++ b/forge/forge/speech/gtts.py @@ -15,7 +15,7 @@ class GTTSVoice(VoiceBase): def _setup(self) -> None: pass - def _speech(self, text: str, _: int = 0) -> bool: + def _speech(self, text: str, voice_id: int = 0) -> bool: """Play the given text.""" tts = gtts.gTTS(text) tts.save("speech.mp3") diff --git a/autogpts/forge/forge/speech/macos_tts.py b/forge/forge/speech/macos_tts.py similarity index 79% rename from autogpts/forge/forge/speech/macos_tts.py rename to forge/forge/speech/macos_tts.py index 971848bd20..bd6be75190 100644 --- a/autogpts/forge/forge/speech/macos_tts.py +++ b/forge/forge/speech/macos_tts.py @@ -12,11 +12,11 @@ class MacOSTTS(VoiceBase): def _setup(self) -> None: pass - def _speech(self, text: str, voice_index: int = 0) -> bool: + def _speech(self, text: str, voice_id: int = 0) -> bool: """Play the given text.""" - if voice_index == 0: + if voice_id == 0: subprocess.run(["say", text], shell=False) - elif voice_index == 1: + elif voice_id == 1: subprocess.run(["say", "-v", "Ava (Premium)", text], shell=False) else: subprocess.run(["say", "-v", "Samantha", text], shell=False) diff --git a/autogpts/forge/forge/speech/say.py b/forge/forge/speech/say.py similarity index 100% rename from autogpts/forge/forge/speech/say.py rename to forge/forge/speech/say.py diff --git a/autogpts/forge/forge/speech/stream_elements_speech.py b/forge/forge/speech/stream_elements_speech.py similarity index 95% rename from autogpts/forge/forge/speech/stream_elements_speech.py rename to forge/forge/speech/stream_elements_speech.py index 7c2cad063c..8bce722381 100644 --- a/autogpts/forge/forge/speech/stream_elements_speech.py +++ b/forge/forge/speech/stream_elements_speech.py @@ -24,8 +24,7 @@ class StreamElementsSpeech(VoiceBase): """Setup the voices, API key, etc.""" self.config = config - def _speech(self, text: str, voice: str, _: int = 0) -> bool: - voice = self.config.voice + def _speech(self, text: str, voice_id: int = 0) -> bool: """Speak text using the streamelements API Args: @@ -35,6 +34,7 @@ class StreamElementsSpeech(VoiceBase): Returns: bool: True if the request was successful, False otherwise """ + voice = self.config.voice tts_url = ( f"https://api.streamelements.com/kappa/v2/speech?voice={voice}&text={text}" ) diff --git a/autogpts/forge/forge/utils/const.py b/forge/forge/utils/const.py similarity index 100% rename from autogpts/forge/forge/utils/const.py rename to forge/forge/utils/const.py diff --git a/autogpts/forge/forge/utils/exceptions.py b/forge/forge/utils/exceptions.py similarity index 89% rename from autogpts/forge/forge/utils/exceptions.py rename to forge/forge/utils/exceptions.py index 2075019782..04fe301250 100644 --- a/autogpts/forge/forge/utils/exceptions.py +++ b/forge/forge/utils/exceptions.py @@ -7,7 +7,7 @@ from typing import Optional def get_exception_message(): """Get current exception type and message.""" exc_type, exc_value, _ = sys.exc_info() - exception_message = f"{exc_type.__name__}: {exc_value}" + exception_message = f"{exc_type.__name__}: {exc_value}" if exc_type else exc_value return exception_message @@ -91,13 +91,5 @@ class OperationNotAllowedError(CommandExecutionError): """The agent is not allowed to execute the proposed operation""" -class AccessDeniedError(CommandExecutionError): - """The operation failed because access to a required resource was denied""" - - -class CodeExecutionError(CommandExecutionError): - """The operation (an attempt to run arbitrary code) returned an error""" - - class TooMuchOutputError(CommandExecutionError): """The operation generated more output than what the Agent can process""" diff --git a/autogpts/forge/forge/utils/file_operations.py b/forge/forge/utils/file_operations.py similarity index 97% rename from autogpts/forge/forge/utils/file_operations.py rename to forge/forge/utils/file_operations.py index e9dcae41b9..d022eff409 100644 --- a/autogpts/forge/forge/utils/file_operations.py +++ b/forge/forge/utils/file_operations.py @@ -25,7 +25,7 @@ class TXTParser(ParserStrategy): charset_match = charset_normalizer.from_bytes(file.read()).best() logger.debug( f"Reading {getattr(file, 'name', 'file')} " - f"with encoding '{charset_match.encoding}'" + f"with encoding '{charset_match.encoding if charset_match else None}'" ) return str(charset_match) diff --git a/autogpts/forge/forge/utils/function/code_validation.py b/forge/forge/utils/function/code_validation.py similarity index 100% rename from autogpts/forge/forge/utils/function/code_validation.py rename to forge/forge/utils/function/code_validation.py diff --git a/autogpts/forge/forge/utils/function/exec.py b/forge/forge/utils/function/exec.py similarity index 100% rename from autogpts/forge/forge/utils/function/exec.py rename to forge/forge/utils/function/exec.py diff --git a/autogpts/forge/forge/utils/function/model.py b/forge/forge/utils/function/model.py similarity index 100% rename from autogpts/forge/forge/utils/function/model.py rename to forge/forge/utils/function/model.py diff --git a/autogpts/forge/forge/utils/function/util.py b/forge/forge/utils/function/util.py similarity index 100% rename from autogpts/forge/forge/utils/function/util.py rename to forge/forge/utils/function/util.py diff --git a/autogpts/forge/forge/utils/function/visitor.py b/forge/forge/utils/function/visitor.py similarity index 100% rename from autogpts/forge/forge/utils/function/visitor.py rename to forge/forge/utils/function/visitor.py diff --git a/autogpts/forge/forge/utils/url_validator.py b/forge/forge/utils/url_validator.py similarity index 98% rename from autogpts/forge/forge/utils/url_validator.py rename to forge/forge/utils/url_validator.py index a097804f15..16536ef65b 100644 --- a/autogpts/forge/forge/utils/url_validator.py +++ b/forge/forge/utils/url_validator.py @@ -39,7 +39,7 @@ def validate_url(func: Callable[P, T]) -> Callable[P, T]: return func(*bound_args.args, **bound_args.kwargs) - return wrapper + return wrapper # type: ignore def is_valid_url(url: str) -> bool: diff --git a/autogpts/forge/poetry.lock b/forge/poetry.lock similarity index 87% rename from autogpts/forge/poetry.lock rename to forge/poetry.lock index fc6d69dadf..32e02faf2e 100644 --- a/autogpts/forge/poetry.lock +++ b/forge/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "agbenchmark" @@ -38,7 +38,7 @@ uvicorn = "^0.23.2" [package.source] type = "directory" -url = "../../benchmark" +url = "../benchmark" [[package]] name = "agent-protocol-client" @@ -278,21 +278,6 @@ tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] -[[package]] -name = "autoflake" -version = "2.2.1" -description = "Removes unused imports and unused variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "autoflake-2.2.1-py3-none-any.whl", hash = "sha256:265cde0a43c1f44ecfb4f30d95b0437796759d07be7706a2f70e4719234c0f79"}, - {file = "autoflake-2.2.1.tar.gz", hash = "sha256:62b7b6449a692c3c9b0c916919bbc21648da7281e8506bcf8d3f8280e431ebc1"}, -] - -[package.dependencies] -pyflakes = ">=3.0.0" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - [[package]] name = "backoff" version = "2.2.1" @@ -473,6 +458,412 @@ s3transfer = ">=0.10.0,<0.11.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] +[[package]] +name = "boto3-stubs" +version = "1.34.113" +description = "Type annotations for boto3 1.34.113 generated with mypy-boto3-builder 7.24.0" +optional = false +python-versions = ">=3.8" +files = [ + {file = "boto3_stubs-1.34.113-py3-none-any.whl", hash = "sha256:14b59f9db03fb19c1cbca5fbef58e70787345ae9db6845a2ec7eb796d43a790c"}, + {file = "boto3_stubs-1.34.113.tar.gz", hash = "sha256:16aaf8d0979cfd65f850536a2ccf4344d00f12638aa377a65b76c00338f35c50"}, +] + +[package.dependencies] +botocore-stubs = "*" +mypy-boto3-s3 = {version = ">=1.34.0,<1.35.0", optional = true, markers = "extra == \"s3\""} +types-s3transfer = "*" +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} + +[package.extras] +accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.34.0,<1.35.0)"] +account = ["mypy-boto3-account (>=1.34.0,<1.35.0)"] +acm = ["mypy-boto3-acm (>=1.34.0,<1.35.0)"] +acm-pca = ["mypy-boto3-acm-pca (>=1.34.0,<1.35.0)"] +alexaforbusiness = ["mypy-boto3-alexaforbusiness (>=1.34.0,<1.35.0)"] +all = ["mypy-boto3-accessanalyzer (>=1.34.0,<1.35.0)", "mypy-boto3-account (>=1.34.0,<1.35.0)", "mypy-boto3-acm (>=1.34.0,<1.35.0)", "mypy-boto3-acm-pca (>=1.34.0,<1.35.0)", "mypy-boto3-alexaforbusiness (>=1.34.0,<1.35.0)", "mypy-boto3-amp (>=1.34.0,<1.35.0)", "mypy-boto3-amplify (>=1.34.0,<1.35.0)", "mypy-boto3-amplifybackend (>=1.34.0,<1.35.0)", "mypy-boto3-amplifyuibuilder (>=1.34.0,<1.35.0)", "mypy-boto3-apigateway (>=1.34.0,<1.35.0)", "mypy-boto3-apigatewaymanagementapi (>=1.34.0,<1.35.0)", "mypy-boto3-apigatewayv2 (>=1.34.0,<1.35.0)", "mypy-boto3-appconfig (>=1.34.0,<1.35.0)", "mypy-boto3-appconfigdata (>=1.34.0,<1.35.0)", "mypy-boto3-appfabric (>=1.34.0,<1.35.0)", "mypy-boto3-appflow (>=1.34.0,<1.35.0)", "mypy-boto3-appintegrations (>=1.34.0,<1.35.0)", "mypy-boto3-application-autoscaling (>=1.34.0,<1.35.0)", "mypy-boto3-application-insights (>=1.34.0,<1.35.0)", "mypy-boto3-applicationcostprofiler (>=1.34.0,<1.35.0)", "mypy-boto3-appmesh (>=1.34.0,<1.35.0)", "mypy-boto3-apprunner (>=1.34.0,<1.35.0)", "mypy-boto3-appstream (>=1.34.0,<1.35.0)", "mypy-boto3-appsync (>=1.34.0,<1.35.0)", "mypy-boto3-arc-zonal-shift (>=1.34.0,<1.35.0)", "mypy-boto3-artifact (>=1.34.0,<1.35.0)", "mypy-boto3-athena (>=1.34.0,<1.35.0)", "mypy-boto3-auditmanager (>=1.34.0,<1.35.0)", "mypy-boto3-autoscaling (>=1.34.0,<1.35.0)", "mypy-boto3-autoscaling-plans (>=1.34.0,<1.35.0)", "mypy-boto3-b2bi (>=1.34.0,<1.35.0)", "mypy-boto3-backup (>=1.34.0,<1.35.0)", "mypy-boto3-backup-gateway (>=1.34.0,<1.35.0)", "mypy-boto3-backupstorage (>=1.34.0,<1.35.0)", "mypy-boto3-batch (>=1.34.0,<1.35.0)", "mypy-boto3-bcm-data-exports (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-agent (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-agent-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-bedrock-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-billingconductor (>=1.34.0,<1.35.0)", "mypy-boto3-braket (>=1.34.0,<1.35.0)", "mypy-boto3-budgets (>=1.34.0,<1.35.0)", "mypy-boto3-ce (>=1.34.0,<1.35.0)", "mypy-boto3-chatbot (>=1.34.0,<1.35.0)", "mypy-boto3-chime (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-identity (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-meetings (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-messaging (>=1.34.0,<1.35.0)", "mypy-boto3-chime-sdk-voice (>=1.34.0,<1.35.0)", "mypy-boto3-cleanrooms (>=1.34.0,<1.35.0)", "mypy-boto3-cleanroomsml (>=1.34.0,<1.35.0)", "mypy-boto3-cloud9 (>=1.34.0,<1.35.0)", "mypy-boto3-cloudcontrol (>=1.34.0,<1.35.0)", "mypy-boto3-clouddirectory (>=1.34.0,<1.35.0)", "mypy-boto3-cloudformation (>=1.34.0,<1.35.0)", "mypy-boto3-cloudfront (>=1.34.0,<1.35.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.34.0,<1.35.0)", "mypy-boto3-cloudhsm (>=1.34.0,<1.35.0)", "mypy-boto3-cloudhsmv2 (>=1.34.0,<1.35.0)", "mypy-boto3-cloudsearch (>=1.34.0,<1.35.0)", "mypy-boto3-cloudsearchdomain (>=1.34.0,<1.35.0)", "mypy-boto3-cloudtrail (>=1.34.0,<1.35.0)", "mypy-boto3-cloudtrail-data (>=1.34.0,<1.35.0)", "mypy-boto3-cloudwatch (>=1.34.0,<1.35.0)", "mypy-boto3-codeartifact (>=1.34.0,<1.35.0)", "mypy-boto3-codebuild (>=1.34.0,<1.35.0)", "mypy-boto3-codecatalyst (>=1.34.0,<1.35.0)", "mypy-boto3-codecommit (>=1.34.0,<1.35.0)", "mypy-boto3-codeconnections (>=1.34.0,<1.35.0)", "mypy-boto3-codedeploy (>=1.34.0,<1.35.0)", "mypy-boto3-codeguru-reviewer (>=1.34.0,<1.35.0)", "mypy-boto3-codeguru-security (>=1.34.0,<1.35.0)", "mypy-boto3-codeguruprofiler (>=1.34.0,<1.35.0)", "mypy-boto3-codepipeline (>=1.34.0,<1.35.0)", "mypy-boto3-codestar (>=1.34.0,<1.35.0)", "mypy-boto3-codestar-connections (>=1.34.0,<1.35.0)", "mypy-boto3-codestar-notifications (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-identity (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-idp (>=1.34.0,<1.35.0)", "mypy-boto3-cognito-sync (>=1.34.0,<1.35.0)", "mypy-boto3-comprehend (>=1.34.0,<1.35.0)", "mypy-boto3-comprehendmedical (>=1.34.0,<1.35.0)", "mypy-boto3-compute-optimizer (>=1.34.0,<1.35.0)", "mypy-boto3-config (>=1.34.0,<1.35.0)", "mypy-boto3-connect (>=1.34.0,<1.35.0)", "mypy-boto3-connect-contact-lens (>=1.34.0,<1.35.0)", "mypy-boto3-connectcampaigns (>=1.34.0,<1.35.0)", "mypy-boto3-connectcases (>=1.34.0,<1.35.0)", "mypy-boto3-connectparticipant (>=1.34.0,<1.35.0)", "mypy-boto3-controlcatalog (>=1.34.0,<1.35.0)", "mypy-boto3-controltower (>=1.34.0,<1.35.0)", "mypy-boto3-cost-optimization-hub (>=1.34.0,<1.35.0)", "mypy-boto3-cur (>=1.34.0,<1.35.0)", "mypy-boto3-customer-profiles (>=1.34.0,<1.35.0)", "mypy-boto3-databrew (>=1.34.0,<1.35.0)", "mypy-boto3-dataexchange (>=1.34.0,<1.35.0)", "mypy-boto3-datapipeline (>=1.34.0,<1.35.0)", "mypy-boto3-datasync (>=1.34.0,<1.35.0)", "mypy-boto3-datazone (>=1.34.0,<1.35.0)", "mypy-boto3-dax (>=1.34.0,<1.35.0)", "mypy-boto3-deadline (>=1.34.0,<1.35.0)", "mypy-boto3-detective (>=1.34.0,<1.35.0)", "mypy-boto3-devicefarm (>=1.34.0,<1.35.0)", "mypy-boto3-devops-guru (>=1.34.0,<1.35.0)", "mypy-boto3-directconnect (>=1.34.0,<1.35.0)", "mypy-boto3-discovery (>=1.34.0,<1.35.0)", "mypy-boto3-dlm (>=1.34.0,<1.35.0)", "mypy-boto3-dms (>=1.34.0,<1.35.0)", "mypy-boto3-docdb (>=1.34.0,<1.35.0)", "mypy-boto3-docdb-elastic (>=1.34.0,<1.35.0)", "mypy-boto3-drs (>=1.34.0,<1.35.0)", "mypy-boto3-ds (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodb (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodbstreams (>=1.34.0,<1.35.0)", "mypy-boto3-ebs (>=1.34.0,<1.35.0)", "mypy-boto3-ec2 (>=1.34.0,<1.35.0)", "mypy-boto3-ec2-instance-connect (>=1.34.0,<1.35.0)", "mypy-boto3-ecr (>=1.34.0,<1.35.0)", "mypy-boto3-ecr-public (>=1.34.0,<1.35.0)", "mypy-boto3-ecs (>=1.34.0,<1.35.0)", "mypy-boto3-efs (>=1.34.0,<1.35.0)", "mypy-boto3-eks (>=1.34.0,<1.35.0)", "mypy-boto3-eks-auth (>=1.34.0,<1.35.0)", "mypy-boto3-elastic-inference (>=1.34.0,<1.35.0)", "mypy-boto3-elasticache (>=1.34.0,<1.35.0)", "mypy-boto3-elasticbeanstalk (>=1.34.0,<1.35.0)", "mypy-boto3-elastictranscoder (>=1.34.0,<1.35.0)", "mypy-boto3-elb (>=1.34.0,<1.35.0)", "mypy-boto3-elbv2 (>=1.34.0,<1.35.0)", "mypy-boto3-emr (>=1.34.0,<1.35.0)", "mypy-boto3-emr-containers (>=1.34.0,<1.35.0)", "mypy-boto3-emr-serverless (>=1.34.0,<1.35.0)", "mypy-boto3-entityresolution (>=1.34.0,<1.35.0)", "mypy-boto3-es (>=1.34.0,<1.35.0)", "mypy-boto3-events (>=1.34.0,<1.35.0)", "mypy-boto3-evidently (>=1.34.0,<1.35.0)", "mypy-boto3-finspace (>=1.34.0,<1.35.0)", "mypy-boto3-finspace-data (>=1.34.0,<1.35.0)", "mypy-boto3-firehose (>=1.34.0,<1.35.0)", "mypy-boto3-fis (>=1.34.0,<1.35.0)", "mypy-boto3-fms (>=1.34.0,<1.35.0)", "mypy-boto3-forecast (>=1.34.0,<1.35.0)", "mypy-boto3-forecastquery (>=1.34.0,<1.35.0)", "mypy-boto3-frauddetector (>=1.34.0,<1.35.0)", "mypy-boto3-freetier (>=1.34.0,<1.35.0)", "mypy-boto3-fsx (>=1.34.0,<1.35.0)", "mypy-boto3-gamelift (>=1.34.0,<1.35.0)", "mypy-boto3-glacier (>=1.34.0,<1.35.0)", "mypy-boto3-globalaccelerator (>=1.34.0,<1.35.0)", "mypy-boto3-glue (>=1.34.0,<1.35.0)", "mypy-boto3-grafana (>=1.34.0,<1.35.0)", "mypy-boto3-greengrass (>=1.34.0,<1.35.0)", "mypy-boto3-greengrassv2 (>=1.34.0,<1.35.0)", "mypy-boto3-groundstation (>=1.34.0,<1.35.0)", "mypy-boto3-guardduty (>=1.34.0,<1.35.0)", "mypy-boto3-health (>=1.34.0,<1.35.0)", "mypy-boto3-healthlake (>=1.34.0,<1.35.0)", "mypy-boto3-honeycode (>=1.34.0,<1.35.0)", "mypy-boto3-iam (>=1.34.0,<1.35.0)", "mypy-boto3-identitystore (>=1.34.0,<1.35.0)", "mypy-boto3-imagebuilder (>=1.34.0,<1.35.0)", "mypy-boto3-importexport (>=1.34.0,<1.35.0)", "mypy-boto3-inspector (>=1.34.0,<1.35.0)", "mypy-boto3-inspector-scan (>=1.34.0,<1.35.0)", "mypy-boto3-inspector2 (>=1.34.0,<1.35.0)", "mypy-boto3-internetmonitor (>=1.34.0,<1.35.0)", "mypy-boto3-iot (>=1.34.0,<1.35.0)", "mypy-boto3-iot-data (>=1.34.0,<1.35.0)", "mypy-boto3-iot-jobs-data (>=1.34.0,<1.35.0)", "mypy-boto3-iot1click-devices (>=1.34.0,<1.35.0)", "mypy-boto3-iot1click-projects (>=1.34.0,<1.35.0)", "mypy-boto3-iotanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-iotdeviceadvisor (>=1.34.0,<1.35.0)", "mypy-boto3-iotevents (>=1.34.0,<1.35.0)", "mypy-boto3-iotevents-data (>=1.34.0,<1.35.0)", "mypy-boto3-iotfleethub (>=1.34.0,<1.35.0)", "mypy-boto3-iotfleetwise (>=1.34.0,<1.35.0)", "mypy-boto3-iotsecuretunneling (>=1.34.0,<1.35.0)", "mypy-boto3-iotsitewise (>=1.34.0,<1.35.0)", "mypy-boto3-iotthingsgraph (>=1.34.0,<1.35.0)", "mypy-boto3-iottwinmaker (>=1.34.0,<1.35.0)", "mypy-boto3-iotwireless (>=1.34.0,<1.35.0)", "mypy-boto3-ivs (>=1.34.0,<1.35.0)", "mypy-boto3-ivs-realtime (>=1.34.0,<1.35.0)", "mypy-boto3-ivschat (>=1.34.0,<1.35.0)", "mypy-boto3-kafka (>=1.34.0,<1.35.0)", "mypy-boto3-kafkaconnect (>=1.34.0,<1.35.0)", "mypy-boto3-kendra (>=1.34.0,<1.35.0)", "mypy-boto3-kendra-ranking (>=1.34.0,<1.35.0)", "mypy-boto3-keyspaces (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-archived-media (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-media (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-signaling (>=1.34.0,<1.35.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.34.0,<1.35.0)", "mypy-boto3-kinesisvideo (>=1.34.0,<1.35.0)", "mypy-boto3-kms (>=1.34.0,<1.35.0)", "mypy-boto3-lakeformation (>=1.34.0,<1.35.0)", "mypy-boto3-lambda (>=1.34.0,<1.35.0)", "mypy-boto3-launch-wizard (>=1.34.0,<1.35.0)", "mypy-boto3-lex-models (>=1.34.0,<1.35.0)", "mypy-boto3-lex-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-lexv2-models (>=1.34.0,<1.35.0)", "mypy-boto3-lexv2-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.34.0,<1.35.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.34.0,<1.35.0)", "mypy-boto3-lightsail (>=1.34.0,<1.35.0)", "mypy-boto3-location (>=1.34.0,<1.35.0)", "mypy-boto3-logs (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutequipment (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutmetrics (>=1.34.0,<1.35.0)", "mypy-boto3-lookoutvision (>=1.34.0,<1.35.0)", "mypy-boto3-m2 (>=1.34.0,<1.35.0)", "mypy-boto3-machinelearning (>=1.34.0,<1.35.0)", "mypy-boto3-macie2 (>=1.34.0,<1.35.0)", "mypy-boto3-mailmanager (>=1.34.0,<1.35.0)", "mypy-boto3-managedblockchain (>=1.34.0,<1.35.0)", "mypy-boto3-managedblockchain-query (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-agreement (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-catalog (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-deployment (>=1.34.0,<1.35.0)", "mypy-boto3-marketplace-entitlement (>=1.34.0,<1.35.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.34.0,<1.35.0)", "mypy-boto3-mediaconnect (>=1.34.0,<1.35.0)", "mypy-boto3-mediaconvert (>=1.34.0,<1.35.0)", "mypy-boto3-medialive (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackage (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackage-vod (>=1.34.0,<1.35.0)", "mypy-boto3-mediapackagev2 (>=1.34.0,<1.35.0)", "mypy-boto3-mediastore (>=1.34.0,<1.35.0)", "mypy-boto3-mediastore-data (>=1.34.0,<1.35.0)", "mypy-boto3-mediatailor (>=1.34.0,<1.35.0)", "mypy-boto3-medical-imaging (>=1.34.0,<1.35.0)", "mypy-boto3-memorydb (>=1.34.0,<1.35.0)", "mypy-boto3-meteringmarketplace (>=1.34.0,<1.35.0)", "mypy-boto3-mgh (>=1.34.0,<1.35.0)", "mypy-boto3-mgn (>=1.34.0,<1.35.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhub-config (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhuborchestrator (>=1.34.0,<1.35.0)", "mypy-boto3-migrationhubstrategy (>=1.34.0,<1.35.0)", "mypy-boto3-mobile (>=1.34.0,<1.35.0)", "mypy-boto3-mq (>=1.34.0,<1.35.0)", "mypy-boto3-mturk (>=1.34.0,<1.35.0)", "mypy-boto3-mwaa (>=1.34.0,<1.35.0)", "mypy-boto3-neptune (>=1.34.0,<1.35.0)", "mypy-boto3-neptune-graph (>=1.34.0,<1.35.0)", "mypy-boto3-neptunedata (>=1.34.0,<1.35.0)", "mypy-boto3-network-firewall (>=1.34.0,<1.35.0)", "mypy-boto3-networkmanager (>=1.34.0,<1.35.0)", "mypy-boto3-networkmonitor (>=1.34.0,<1.35.0)", "mypy-boto3-nimble (>=1.34.0,<1.35.0)", "mypy-boto3-oam (>=1.34.0,<1.35.0)", "mypy-boto3-omics (>=1.34.0,<1.35.0)", "mypy-boto3-opensearch (>=1.34.0,<1.35.0)", "mypy-boto3-opensearchserverless (>=1.34.0,<1.35.0)", "mypy-boto3-opsworks (>=1.34.0,<1.35.0)", "mypy-boto3-opsworkscm (>=1.34.0,<1.35.0)", "mypy-boto3-organizations (>=1.34.0,<1.35.0)", "mypy-boto3-osis (>=1.34.0,<1.35.0)", "mypy-boto3-outposts (>=1.34.0,<1.35.0)", "mypy-boto3-panorama (>=1.34.0,<1.35.0)", "mypy-boto3-payment-cryptography (>=1.34.0,<1.35.0)", "mypy-boto3-payment-cryptography-data (>=1.34.0,<1.35.0)", "mypy-boto3-pca-connector-ad (>=1.34.0,<1.35.0)", "mypy-boto3-personalize (>=1.34.0,<1.35.0)", "mypy-boto3-personalize-events (>=1.34.0,<1.35.0)", "mypy-boto3-personalize-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-pi (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-email (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-sms-voice (>=1.34.0,<1.35.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.34.0,<1.35.0)", "mypy-boto3-pipes (>=1.34.0,<1.35.0)", "mypy-boto3-polly (>=1.34.0,<1.35.0)", "mypy-boto3-pricing (>=1.34.0,<1.35.0)", "mypy-boto3-privatenetworks (>=1.34.0,<1.35.0)", "mypy-boto3-proton (>=1.34.0,<1.35.0)", "mypy-boto3-qbusiness (>=1.34.0,<1.35.0)", "mypy-boto3-qconnect (>=1.34.0,<1.35.0)", "mypy-boto3-qldb (>=1.34.0,<1.35.0)", "mypy-boto3-qldb-session (>=1.34.0,<1.35.0)", "mypy-boto3-quicksight (>=1.34.0,<1.35.0)", "mypy-boto3-ram (>=1.34.0,<1.35.0)", "mypy-boto3-rbin (>=1.34.0,<1.35.0)", "mypy-boto3-rds (>=1.34.0,<1.35.0)", "mypy-boto3-rds-data (>=1.34.0,<1.35.0)", "mypy-boto3-redshift (>=1.34.0,<1.35.0)", "mypy-boto3-redshift-data (>=1.34.0,<1.35.0)", "mypy-boto3-redshift-serverless (>=1.34.0,<1.35.0)", "mypy-boto3-rekognition (>=1.34.0,<1.35.0)", "mypy-boto3-repostspace (>=1.34.0,<1.35.0)", "mypy-boto3-resiliencehub (>=1.34.0,<1.35.0)", "mypy-boto3-resource-explorer-2 (>=1.34.0,<1.35.0)", "mypy-boto3-resource-groups (>=1.34.0,<1.35.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.34.0,<1.35.0)", "mypy-boto3-robomaker (>=1.34.0,<1.35.0)", "mypy-boto3-rolesanywhere (>=1.34.0,<1.35.0)", "mypy-boto3-route53 (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-cluster (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-control-config (>=1.34.0,<1.35.0)", "mypy-boto3-route53-recovery-readiness (>=1.34.0,<1.35.0)", "mypy-boto3-route53domains (>=1.34.0,<1.35.0)", "mypy-boto3-route53profiles (>=1.34.0,<1.35.0)", "mypy-boto3-route53resolver (>=1.34.0,<1.35.0)", "mypy-boto3-rum (>=1.34.0,<1.35.0)", "mypy-boto3-s3 (>=1.34.0,<1.35.0)", "mypy-boto3-s3control (>=1.34.0,<1.35.0)", "mypy-boto3-s3outposts (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-edge (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-geospatial (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-metrics (>=1.34.0,<1.35.0)", "mypy-boto3-sagemaker-runtime (>=1.34.0,<1.35.0)", "mypy-boto3-savingsplans (>=1.34.0,<1.35.0)", "mypy-boto3-scheduler (>=1.34.0,<1.35.0)", "mypy-boto3-schemas (>=1.34.0,<1.35.0)", "mypy-boto3-sdb (>=1.34.0,<1.35.0)", "mypy-boto3-secretsmanager (>=1.34.0,<1.35.0)", "mypy-boto3-securityhub (>=1.34.0,<1.35.0)", "mypy-boto3-securitylake (>=1.34.0,<1.35.0)", "mypy-boto3-serverlessrepo (>=1.34.0,<1.35.0)", "mypy-boto3-service-quotas (>=1.34.0,<1.35.0)", "mypy-boto3-servicecatalog (>=1.34.0,<1.35.0)", "mypy-boto3-servicecatalog-appregistry (>=1.34.0,<1.35.0)", "mypy-boto3-servicediscovery (>=1.34.0,<1.35.0)", "mypy-boto3-ses (>=1.34.0,<1.35.0)", "mypy-boto3-sesv2 (>=1.34.0,<1.35.0)", "mypy-boto3-shield (>=1.34.0,<1.35.0)", "mypy-boto3-signer (>=1.34.0,<1.35.0)", "mypy-boto3-simspaceweaver (>=1.34.0,<1.35.0)", "mypy-boto3-sms (>=1.34.0,<1.35.0)", "mypy-boto3-sms-voice (>=1.34.0,<1.35.0)", "mypy-boto3-snow-device-management (>=1.34.0,<1.35.0)", "mypy-boto3-snowball (>=1.34.0,<1.35.0)", "mypy-boto3-sns (>=1.34.0,<1.35.0)", "mypy-boto3-sqs (>=1.34.0,<1.35.0)", "mypy-boto3-ssm (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-contacts (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-incidents (>=1.34.0,<1.35.0)", "mypy-boto3-ssm-sap (>=1.34.0,<1.35.0)", "mypy-boto3-sso (>=1.34.0,<1.35.0)", "mypy-boto3-sso-admin (>=1.34.0,<1.35.0)", "mypy-boto3-sso-oidc (>=1.34.0,<1.35.0)", "mypy-boto3-stepfunctions (>=1.34.0,<1.35.0)", "mypy-boto3-storagegateway (>=1.34.0,<1.35.0)", "mypy-boto3-sts (>=1.34.0,<1.35.0)", "mypy-boto3-supplychain (>=1.34.0,<1.35.0)", "mypy-boto3-support (>=1.34.0,<1.35.0)", "mypy-boto3-support-app (>=1.34.0,<1.35.0)", "mypy-boto3-swf (>=1.34.0,<1.35.0)", "mypy-boto3-synthetics (>=1.34.0,<1.35.0)", "mypy-boto3-textract (>=1.34.0,<1.35.0)", "mypy-boto3-timestream-influxdb (>=1.34.0,<1.35.0)", "mypy-boto3-timestream-query (>=1.34.0,<1.35.0)", "mypy-boto3-timestream-write (>=1.34.0,<1.35.0)", "mypy-boto3-tnb (>=1.34.0,<1.35.0)", "mypy-boto3-transcribe (>=1.34.0,<1.35.0)", "mypy-boto3-transfer (>=1.34.0,<1.35.0)", "mypy-boto3-translate (>=1.34.0,<1.35.0)", "mypy-boto3-trustedadvisor (>=1.34.0,<1.35.0)", "mypy-boto3-verifiedpermissions (>=1.34.0,<1.35.0)", "mypy-boto3-voice-id (>=1.34.0,<1.35.0)", "mypy-boto3-vpc-lattice (>=1.34.0,<1.35.0)", "mypy-boto3-waf (>=1.34.0,<1.35.0)", "mypy-boto3-waf-regional (>=1.34.0,<1.35.0)", "mypy-boto3-wafv2 (>=1.34.0,<1.35.0)", "mypy-boto3-wellarchitected (>=1.34.0,<1.35.0)", "mypy-boto3-wisdom (>=1.34.0,<1.35.0)", "mypy-boto3-workdocs (>=1.34.0,<1.35.0)", "mypy-boto3-worklink (>=1.34.0,<1.35.0)", "mypy-boto3-workmail (>=1.34.0,<1.35.0)", "mypy-boto3-workmailmessageflow (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces-thin-client (>=1.34.0,<1.35.0)", "mypy-boto3-workspaces-web (>=1.34.0,<1.35.0)", "mypy-boto3-xray (>=1.34.0,<1.35.0)"] +amp = ["mypy-boto3-amp (>=1.34.0,<1.35.0)"] +amplify = ["mypy-boto3-amplify (>=1.34.0,<1.35.0)"] +amplifybackend = ["mypy-boto3-amplifybackend (>=1.34.0,<1.35.0)"] +amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.34.0,<1.35.0)"] +apigateway = ["mypy-boto3-apigateway (>=1.34.0,<1.35.0)"] +apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.34.0,<1.35.0)"] +apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.34.0,<1.35.0)"] +appconfig = ["mypy-boto3-appconfig (>=1.34.0,<1.35.0)"] +appconfigdata = ["mypy-boto3-appconfigdata (>=1.34.0,<1.35.0)"] +appfabric = ["mypy-boto3-appfabric (>=1.34.0,<1.35.0)"] +appflow = ["mypy-boto3-appflow (>=1.34.0,<1.35.0)"] +appintegrations = ["mypy-boto3-appintegrations (>=1.34.0,<1.35.0)"] +application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.34.0,<1.35.0)"] +application-insights = ["mypy-boto3-application-insights (>=1.34.0,<1.35.0)"] +applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.34.0,<1.35.0)"] +appmesh = ["mypy-boto3-appmesh (>=1.34.0,<1.35.0)"] +apprunner = ["mypy-boto3-apprunner (>=1.34.0,<1.35.0)"] +appstream = ["mypy-boto3-appstream (>=1.34.0,<1.35.0)"] +appsync = ["mypy-boto3-appsync (>=1.34.0,<1.35.0)"] +arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.34.0,<1.35.0)"] +artifact = ["mypy-boto3-artifact (>=1.34.0,<1.35.0)"] +athena = ["mypy-boto3-athena (>=1.34.0,<1.35.0)"] +auditmanager = ["mypy-boto3-auditmanager (>=1.34.0,<1.35.0)"] +autoscaling = ["mypy-boto3-autoscaling (>=1.34.0,<1.35.0)"] +autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.34.0,<1.35.0)"] +b2bi = ["mypy-boto3-b2bi (>=1.34.0,<1.35.0)"] +backup = ["mypy-boto3-backup (>=1.34.0,<1.35.0)"] +backup-gateway = ["mypy-boto3-backup-gateway (>=1.34.0,<1.35.0)"] +backupstorage = ["mypy-boto3-backupstorage (>=1.34.0,<1.35.0)"] +batch = ["mypy-boto3-batch (>=1.34.0,<1.35.0)"] +bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.34.0,<1.35.0)"] +bedrock = ["mypy-boto3-bedrock (>=1.34.0,<1.35.0)"] +bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.34.0,<1.35.0)"] +bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.34.0,<1.35.0)"] +bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.34.0,<1.35.0)"] +billingconductor = ["mypy-boto3-billingconductor (>=1.34.0,<1.35.0)"] +boto3 = ["boto3 (==1.34.113)", "botocore (==1.34.113)"] +braket = ["mypy-boto3-braket (>=1.34.0,<1.35.0)"] +budgets = ["mypy-boto3-budgets (>=1.34.0,<1.35.0)"] +ce = ["mypy-boto3-ce (>=1.34.0,<1.35.0)"] +chatbot = ["mypy-boto3-chatbot (>=1.34.0,<1.35.0)"] +chime = ["mypy-boto3-chime (>=1.34.0,<1.35.0)"] +chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.34.0,<1.35.0)"] +chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.34.0,<1.35.0)"] +chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.34.0,<1.35.0)"] +chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.34.0,<1.35.0)"] +chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.34.0,<1.35.0)"] +cleanrooms = ["mypy-boto3-cleanrooms (>=1.34.0,<1.35.0)"] +cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.34.0,<1.35.0)"] +cloud9 = ["mypy-boto3-cloud9 (>=1.34.0,<1.35.0)"] +cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.34.0,<1.35.0)"] +clouddirectory = ["mypy-boto3-clouddirectory (>=1.34.0,<1.35.0)"] +cloudformation = ["mypy-boto3-cloudformation (>=1.34.0,<1.35.0)"] +cloudfront = ["mypy-boto3-cloudfront (>=1.34.0,<1.35.0)"] +cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.34.0,<1.35.0)"] +cloudhsm = ["mypy-boto3-cloudhsm (>=1.34.0,<1.35.0)"] +cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.34.0,<1.35.0)"] +cloudsearch = ["mypy-boto3-cloudsearch (>=1.34.0,<1.35.0)"] +cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.34.0,<1.35.0)"] +cloudtrail = ["mypy-boto3-cloudtrail (>=1.34.0,<1.35.0)"] +cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.34.0,<1.35.0)"] +cloudwatch = ["mypy-boto3-cloudwatch (>=1.34.0,<1.35.0)"] +codeartifact = ["mypy-boto3-codeartifact (>=1.34.0,<1.35.0)"] +codebuild = ["mypy-boto3-codebuild (>=1.34.0,<1.35.0)"] +codecatalyst = ["mypy-boto3-codecatalyst (>=1.34.0,<1.35.0)"] +codecommit = ["mypy-boto3-codecommit (>=1.34.0,<1.35.0)"] +codeconnections = ["mypy-boto3-codeconnections (>=1.34.0,<1.35.0)"] +codedeploy = ["mypy-boto3-codedeploy (>=1.34.0,<1.35.0)"] +codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.34.0,<1.35.0)"] +codeguru-security = ["mypy-boto3-codeguru-security (>=1.34.0,<1.35.0)"] +codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.34.0,<1.35.0)"] +codepipeline = ["mypy-boto3-codepipeline (>=1.34.0,<1.35.0)"] +codestar = ["mypy-boto3-codestar (>=1.34.0,<1.35.0)"] +codestar-connections = ["mypy-boto3-codestar-connections (>=1.34.0,<1.35.0)"] +codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.34.0,<1.35.0)"] +cognito-identity = ["mypy-boto3-cognito-identity (>=1.34.0,<1.35.0)"] +cognito-idp = ["mypy-boto3-cognito-idp (>=1.34.0,<1.35.0)"] +cognito-sync = ["mypy-boto3-cognito-sync (>=1.34.0,<1.35.0)"] +comprehend = ["mypy-boto3-comprehend (>=1.34.0,<1.35.0)"] +comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.34.0,<1.35.0)"] +compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.34.0,<1.35.0)"] +config = ["mypy-boto3-config (>=1.34.0,<1.35.0)"] +connect = ["mypy-boto3-connect (>=1.34.0,<1.35.0)"] +connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.34.0,<1.35.0)"] +connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.34.0,<1.35.0)"] +connectcases = ["mypy-boto3-connectcases (>=1.34.0,<1.35.0)"] +connectparticipant = ["mypy-boto3-connectparticipant (>=1.34.0,<1.35.0)"] +controlcatalog = ["mypy-boto3-controlcatalog (>=1.34.0,<1.35.0)"] +controltower = ["mypy-boto3-controltower (>=1.34.0,<1.35.0)"] +cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.34.0,<1.35.0)"] +cur = ["mypy-boto3-cur (>=1.34.0,<1.35.0)"] +customer-profiles = ["mypy-boto3-customer-profiles (>=1.34.0,<1.35.0)"] +databrew = ["mypy-boto3-databrew (>=1.34.0,<1.35.0)"] +dataexchange = ["mypy-boto3-dataexchange (>=1.34.0,<1.35.0)"] +datapipeline = ["mypy-boto3-datapipeline (>=1.34.0,<1.35.0)"] +datasync = ["mypy-boto3-datasync (>=1.34.0,<1.35.0)"] +datazone = ["mypy-boto3-datazone (>=1.34.0,<1.35.0)"] +dax = ["mypy-boto3-dax (>=1.34.0,<1.35.0)"] +deadline = ["mypy-boto3-deadline (>=1.34.0,<1.35.0)"] +detective = ["mypy-boto3-detective (>=1.34.0,<1.35.0)"] +devicefarm = ["mypy-boto3-devicefarm (>=1.34.0,<1.35.0)"] +devops-guru = ["mypy-boto3-devops-guru (>=1.34.0,<1.35.0)"] +directconnect = ["mypy-boto3-directconnect (>=1.34.0,<1.35.0)"] +discovery = ["mypy-boto3-discovery (>=1.34.0,<1.35.0)"] +dlm = ["mypy-boto3-dlm (>=1.34.0,<1.35.0)"] +dms = ["mypy-boto3-dms (>=1.34.0,<1.35.0)"] +docdb = ["mypy-boto3-docdb (>=1.34.0,<1.35.0)"] +docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.34.0,<1.35.0)"] +drs = ["mypy-boto3-drs (>=1.34.0,<1.35.0)"] +ds = ["mypy-boto3-ds (>=1.34.0,<1.35.0)"] +dynamodb = ["mypy-boto3-dynamodb (>=1.34.0,<1.35.0)"] +dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.34.0,<1.35.0)"] +ebs = ["mypy-boto3-ebs (>=1.34.0,<1.35.0)"] +ec2 = ["mypy-boto3-ec2 (>=1.34.0,<1.35.0)"] +ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.34.0,<1.35.0)"] +ecr = ["mypy-boto3-ecr (>=1.34.0,<1.35.0)"] +ecr-public = ["mypy-boto3-ecr-public (>=1.34.0,<1.35.0)"] +ecs = ["mypy-boto3-ecs (>=1.34.0,<1.35.0)"] +efs = ["mypy-boto3-efs (>=1.34.0,<1.35.0)"] +eks = ["mypy-boto3-eks (>=1.34.0,<1.35.0)"] +eks-auth = ["mypy-boto3-eks-auth (>=1.34.0,<1.35.0)"] +elastic-inference = ["mypy-boto3-elastic-inference (>=1.34.0,<1.35.0)"] +elasticache = ["mypy-boto3-elasticache (>=1.34.0,<1.35.0)"] +elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.34.0,<1.35.0)"] +elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.34.0,<1.35.0)"] +elb = ["mypy-boto3-elb (>=1.34.0,<1.35.0)"] +elbv2 = ["mypy-boto3-elbv2 (>=1.34.0,<1.35.0)"] +emr = ["mypy-boto3-emr (>=1.34.0,<1.35.0)"] +emr-containers = ["mypy-boto3-emr-containers (>=1.34.0,<1.35.0)"] +emr-serverless = ["mypy-boto3-emr-serverless (>=1.34.0,<1.35.0)"] +entityresolution = ["mypy-boto3-entityresolution (>=1.34.0,<1.35.0)"] +es = ["mypy-boto3-es (>=1.34.0,<1.35.0)"] +essential = ["mypy-boto3-cloudformation (>=1.34.0,<1.35.0)", "mypy-boto3-dynamodb (>=1.34.0,<1.35.0)", "mypy-boto3-ec2 (>=1.34.0,<1.35.0)", "mypy-boto3-lambda (>=1.34.0,<1.35.0)", "mypy-boto3-rds (>=1.34.0,<1.35.0)", "mypy-boto3-s3 (>=1.34.0,<1.35.0)", "mypy-boto3-sqs (>=1.34.0,<1.35.0)"] +events = ["mypy-boto3-events (>=1.34.0,<1.35.0)"] +evidently = ["mypy-boto3-evidently (>=1.34.0,<1.35.0)"] +finspace = ["mypy-boto3-finspace (>=1.34.0,<1.35.0)"] +finspace-data = ["mypy-boto3-finspace-data (>=1.34.0,<1.35.0)"] +firehose = ["mypy-boto3-firehose (>=1.34.0,<1.35.0)"] +fis = ["mypy-boto3-fis (>=1.34.0,<1.35.0)"] +fms = ["mypy-boto3-fms (>=1.34.0,<1.35.0)"] +forecast = ["mypy-boto3-forecast (>=1.34.0,<1.35.0)"] +forecastquery = ["mypy-boto3-forecastquery (>=1.34.0,<1.35.0)"] +frauddetector = ["mypy-boto3-frauddetector (>=1.34.0,<1.35.0)"] +freetier = ["mypy-boto3-freetier (>=1.34.0,<1.35.0)"] +fsx = ["mypy-boto3-fsx (>=1.34.0,<1.35.0)"] +gamelift = ["mypy-boto3-gamelift (>=1.34.0,<1.35.0)"] +glacier = ["mypy-boto3-glacier (>=1.34.0,<1.35.0)"] +globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.34.0,<1.35.0)"] +glue = ["mypy-boto3-glue (>=1.34.0,<1.35.0)"] +grafana = ["mypy-boto3-grafana (>=1.34.0,<1.35.0)"] +greengrass = ["mypy-boto3-greengrass (>=1.34.0,<1.35.0)"] +greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.34.0,<1.35.0)"] +groundstation = ["mypy-boto3-groundstation (>=1.34.0,<1.35.0)"] +guardduty = ["mypy-boto3-guardduty (>=1.34.0,<1.35.0)"] +health = ["mypy-boto3-health (>=1.34.0,<1.35.0)"] +healthlake = ["mypy-boto3-healthlake (>=1.34.0,<1.35.0)"] +honeycode = ["mypy-boto3-honeycode (>=1.34.0,<1.35.0)"] +iam = ["mypy-boto3-iam (>=1.34.0,<1.35.0)"] +identitystore = ["mypy-boto3-identitystore (>=1.34.0,<1.35.0)"] +imagebuilder = ["mypy-boto3-imagebuilder (>=1.34.0,<1.35.0)"] +importexport = ["mypy-boto3-importexport (>=1.34.0,<1.35.0)"] +inspector = ["mypy-boto3-inspector (>=1.34.0,<1.35.0)"] +inspector-scan = ["mypy-boto3-inspector-scan (>=1.34.0,<1.35.0)"] +inspector2 = ["mypy-boto3-inspector2 (>=1.34.0,<1.35.0)"] +internetmonitor = ["mypy-boto3-internetmonitor (>=1.34.0,<1.35.0)"] +iot = ["mypy-boto3-iot (>=1.34.0,<1.35.0)"] +iot-data = ["mypy-boto3-iot-data (>=1.34.0,<1.35.0)"] +iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.34.0,<1.35.0)"] +iot1click-devices = ["mypy-boto3-iot1click-devices (>=1.34.0,<1.35.0)"] +iot1click-projects = ["mypy-boto3-iot1click-projects (>=1.34.0,<1.35.0)"] +iotanalytics = ["mypy-boto3-iotanalytics (>=1.34.0,<1.35.0)"] +iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.34.0,<1.35.0)"] +iotevents = ["mypy-boto3-iotevents (>=1.34.0,<1.35.0)"] +iotevents-data = ["mypy-boto3-iotevents-data (>=1.34.0,<1.35.0)"] +iotfleethub = ["mypy-boto3-iotfleethub (>=1.34.0,<1.35.0)"] +iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.34.0,<1.35.0)"] +iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.34.0,<1.35.0)"] +iotsitewise = ["mypy-boto3-iotsitewise (>=1.34.0,<1.35.0)"] +iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.34.0,<1.35.0)"] +iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.34.0,<1.35.0)"] +iotwireless = ["mypy-boto3-iotwireless (>=1.34.0,<1.35.0)"] +ivs = ["mypy-boto3-ivs (>=1.34.0,<1.35.0)"] +ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.34.0,<1.35.0)"] +ivschat = ["mypy-boto3-ivschat (>=1.34.0,<1.35.0)"] +kafka = ["mypy-boto3-kafka (>=1.34.0,<1.35.0)"] +kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.34.0,<1.35.0)"] +kendra = ["mypy-boto3-kendra (>=1.34.0,<1.35.0)"] +kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.34.0,<1.35.0)"] +keyspaces = ["mypy-boto3-keyspaces (>=1.34.0,<1.35.0)"] +kinesis = ["mypy-boto3-kinesis (>=1.34.0,<1.35.0)"] +kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.34.0,<1.35.0)"] +kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.34.0,<1.35.0)"] +kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.34.0,<1.35.0)"] +kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.34.0,<1.35.0)"] +kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.34.0,<1.35.0)"] +kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.34.0,<1.35.0)"] +kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.34.0,<1.35.0)"] +kms = ["mypy-boto3-kms (>=1.34.0,<1.35.0)"] +lakeformation = ["mypy-boto3-lakeformation (>=1.34.0,<1.35.0)"] +lambda = ["mypy-boto3-lambda (>=1.34.0,<1.35.0)"] +launch-wizard = ["mypy-boto3-launch-wizard (>=1.34.0,<1.35.0)"] +lex-models = ["mypy-boto3-lex-models (>=1.34.0,<1.35.0)"] +lex-runtime = ["mypy-boto3-lex-runtime (>=1.34.0,<1.35.0)"] +lexv2-models = ["mypy-boto3-lexv2-models (>=1.34.0,<1.35.0)"] +lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.34.0,<1.35.0)"] +license-manager = ["mypy-boto3-license-manager (>=1.34.0,<1.35.0)"] +license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.34.0,<1.35.0)"] +license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.34.0,<1.35.0)"] +lightsail = ["mypy-boto3-lightsail (>=1.34.0,<1.35.0)"] +location = ["mypy-boto3-location (>=1.34.0,<1.35.0)"] +logs = ["mypy-boto3-logs (>=1.34.0,<1.35.0)"] +lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.34.0,<1.35.0)"] +lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.34.0,<1.35.0)"] +lookoutvision = ["mypy-boto3-lookoutvision (>=1.34.0,<1.35.0)"] +m2 = ["mypy-boto3-m2 (>=1.34.0,<1.35.0)"] +machinelearning = ["mypy-boto3-machinelearning (>=1.34.0,<1.35.0)"] +macie2 = ["mypy-boto3-macie2 (>=1.34.0,<1.35.0)"] +mailmanager = ["mypy-boto3-mailmanager (>=1.34.0,<1.35.0)"] +managedblockchain = ["mypy-boto3-managedblockchain (>=1.34.0,<1.35.0)"] +managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.34.0,<1.35.0)"] +marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.34.0,<1.35.0)"] +marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.34.0,<1.35.0)"] +marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.34.0,<1.35.0)"] +marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.34.0,<1.35.0)"] +marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.34.0,<1.35.0)"] +mediaconnect = ["mypy-boto3-mediaconnect (>=1.34.0,<1.35.0)"] +mediaconvert = ["mypy-boto3-mediaconvert (>=1.34.0,<1.35.0)"] +medialive = ["mypy-boto3-medialive (>=1.34.0,<1.35.0)"] +mediapackage = ["mypy-boto3-mediapackage (>=1.34.0,<1.35.0)"] +mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.34.0,<1.35.0)"] +mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.34.0,<1.35.0)"] +mediastore = ["mypy-boto3-mediastore (>=1.34.0,<1.35.0)"] +mediastore-data = ["mypy-boto3-mediastore-data (>=1.34.0,<1.35.0)"] +mediatailor = ["mypy-boto3-mediatailor (>=1.34.0,<1.35.0)"] +medical-imaging = ["mypy-boto3-medical-imaging (>=1.34.0,<1.35.0)"] +memorydb = ["mypy-boto3-memorydb (>=1.34.0,<1.35.0)"] +meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.34.0,<1.35.0)"] +mgh = ["mypy-boto3-mgh (>=1.34.0,<1.35.0)"] +mgn = ["mypy-boto3-mgn (>=1.34.0,<1.35.0)"] +migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.34.0,<1.35.0)"] +migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.34.0,<1.35.0)"] +migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.34.0,<1.35.0)"] +migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.34.0,<1.35.0)"] +mobile = ["mypy-boto3-mobile (>=1.34.0,<1.35.0)"] +mq = ["mypy-boto3-mq (>=1.34.0,<1.35.0)"] +mturk = ["mypy-boto3-mturk (>=1.34.0,<1.35.0)"] +mwaa = ["mypy-boto3-mwaa (>=1.34.0,<1.35.0)"] +neptune = ["mypy-boto3-neptune (>=1.34.0,<1.35.0)"] +neptune-graph = ["mypy-boto3-neptune-graph (>=1.34.0,<1.35.0)"] +neptunedata = ["mypy-boto3-neptunedata (>=1.34.0,<1.35.0)"] +network-firewall = ["mypy-boto3-network-firewall (>=1.34.0,<1.35.0)"] +networkmanager = ["mypy-boto3-networkmanager (>=1.34.0,<1.35.0)"] +networkmonitor = ["mypy-boto3-networkmonitor (>=1.34.0,<1.35.0)"] +nimble = ["mypy-boto3-nimble (>=1.34.0,<1.35.0)"] +oam = ["mypy-boto3-oam (>=1.34.0,<1.35.0)"] +omics = ["mypy-boto3-omics (>=1.34.0,<1.35.0)"] +opensearch = ["mypy-boto3-opensearch (>=1.34.0,<1.35.0)"] +opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.34.0,<1.35.0)"] +opsworks = ["mypy-boto3-opsworks (>=1.34.0,<1.35.0)"] +opsworkscm = ["mypy-boto3-opsworkscm (>=1.34.0,<1.35.0)"] +organizations = ["mypy-boto3-organizations (>=1.34.0,<1.35.0)"] +osis = ["mypy-boto3-osis (>=1.34.0,<1.35.0)"] +outposts = ["mypy-boto3-outposts (>=1.34.0,<1.35.0)"] +panorama = ["mypy-boto3-panorama (>=1.34.0,<1.35.0)"] +payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.34.0,<1.35.0)"] +payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.34.0,<1.35.0)"] +pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.34.0,<1.35.0)"] +personalize = ["mypy-boto3-personalize (>=1.34.0,<1.35.0)"] +personalize-events = ["mypy-boto3-personalize-events (>=1.34.0,<1.35.0)"] +personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.34.0,<1.35.0)"] +pi = ["mypy-boto3-pi (>=1.34.0,<1.35.0)"] +pinpoint = ["mypy-boto3-pinpoint (>=1.34.0,<1.35.0)"] +pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.34.0,<1.35.0)"] +pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.34.0,<1.35.0)"] +pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.34.0,<1.35.0)"] +pipes = ["mypy-boto3-pipes (>=1.34.0,<1.35.0)"] +polly = ["mypy-boto3-polly (>=1.34.0,<1.35.0)"] +pricing = ["mypy-boto3-pricing (>=1.34.0,<1.35.0)"] +privatenetworks = ["mypy-boto3-privatenetworks (>=1.34.0,<1.35.0)"] +proton = ["mypy-boto3-proton (>=1.34.0,<1.35.0)"] +qbusiness = ["mypy-boto3-qbusiness (>=1.34.0,<1.35.0)"] +qconnect = ["mypy-boto3-qconnect (>=1.34.0,<1.35.0)"] +qldb = ["mypy-boto3-qldb (>=1.34.0,<1.35.0)"] +qldb-session = ["mypy-boto3-qldb-session (>=1.34.0,<1.35.0)"] +quicksight = ["mypy-boto3-quicksight (>=1.34.0,<1.35.0)"] +ram = ["mypy-boto3-ram (>=1.34.0,<1.35.0)"] +rbin = ["mypy-boto3-rbin (>=1.34.0,<1.35.0)"] +rds = ["mypy-boto3-rds (>=1.34.0,<1.35.0)"] +rds-data = ["mypy-boto3-rds-data (>=1.34.0,<1.35.0)"] +redshift = ["mypy-boto3-redshift (>=1.34.0,<1.35.0)"] +redshift-data = ["mypy-boto3-redshift-data (>=1.34.0,<1.35.0)"] +redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.34.0,<1.35.0)"] +rekognition = ["mypy-boto3-rekognition (>=1.34.0,<1.35.0)"] +repostspace = ["mypy-boto3-repostspace (>=1.34.0,<1.35.0)"] +resiliencehub = ["mypy-boto3-resiliencehub (>=1.34.0,<1.35.0)"] +resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.34.0,<1.35.0)"] +resource-groups = ["mypy-boto3-resource-groups (>=1.34.0,<1.35.0)"] +resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.34.0,<1.35.0)"] +robomaker = ["mypy-boto3-robomaker (>=1.34.0,<1.35.0)"] +rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.34.0,<1.35.0)"] +route53 = ["mypy-boto3-route53 (>=1.34.0,<1.35.0)"] +route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.34.0,<1.35.0)"] +route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.34.0,<1.35.0)"] +route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.34.0,<1.35.0)"] +route53domains = ["mypy-boto3-route53domains (>=1.34.0,<1.35.0)"] +route53profiles = ["mypy-boto3-route53profiles (>=1.34.0,<1.35.0)"] +route53resolver = ["mypy-boto3-route53resolver (>=1.34.0,<1.35.0)"] +rum = ["mypy-boto3-rum (>=1.34.0,<1.35.0)"] +s3 = ["mypy-boto3-s3 (>=1.34.0,<1.35.0)"] +s3control = ["mypy-boto3-s3control (>=1.34.0,<1.35.0)"] +s3outposts = ["mypy-boto3-s3outposts (>=1.34.0,<1.35.0)"] +sagemaker = ["mypy-boto3-sagemaker (>=1.34.0,<1.35.0)"] +sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.34.0,<1.35.0)"] +sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.34.0,<1.35.0)"] +sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.34.0,<1.35.0)"] +sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.34.0,<1.35.0)"] +sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.34.0,<1.35.0)"] +sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.34.0,<1.35.0)"] +savingsplans = ["mypy-boto3-savingsplans (>=1.34.0,<1.35.0)"] +scheduler = ["mypy-boto3-scheduler (>=1.34.0,<1.35.0)"] +schemas = ["mypy-boto3-schemas (>=1.34.0,<1.35.0)"] +sdb = ["mypy-boto3-sdb (>=1.34.0,<1.35.0)"] +secretsmanager = ["mypy-boto3-secretsmanager (>=1.34.0,<1.35.0)"] +securityhub = ["mypy-boto3-securityhub (>=1.34.0,<1.35.0)"] +securitylake = ["mypy-boto3-securitylake (>=1.34.0,<1.35.0)"] +serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.34.0,<1.35.0)"] +service-quotas = ["mypy-boto3-service-quotas (>=1.34.0,<1.35.0)"] +servicecatalog = ["mypy-boto3-servicecatalog (>=1.34.0,<1.35.0)"] +servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.34.0,<1.35.0)"] +servicediscovery = ["mypy-boto3-servicediscovery (>=1.34.0,<1.35.0)"] +ses = ["mypy-boto3-ses (>=1.34.0,<1.35.0)"] +sesv2 = ["mypy-boto3-sesv2 (>=1.34.0,<1.35.0)"] +shield = ["mypy-boto3-shield (>=1.34.0,<1.35.0)"] +signer = ["mypy-boto3-signer (>=1.34.0,<1.35.0)"] +simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.34.0,<1.35.0)"] +sms = ["mypy-boto3-sms (>=1.34.0,<1.35.0)"] +sms-voice = ["mypy-boto3-sms-voice (>=1.34.0,<1.35.0)"] +snow-device-management = ["mypy-boto3-snow-device-management (>=1.34.0,<1.35.0)"] +snowball = ["mypy-boto3-snowball (>=1.34.0,<1.35.0)"] +sns = ["mypy-boto3-sns (>=1.34.0,<1.35.0)"] +sqs = ["mypy-boto3-sqs (>=1.34.0,<1.35.0)"] +ssm = ["mypy-boto3-ssm (>=1.34.0,<1.35.0)"] +ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.34.0,<1.35.0)"] +ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.34.0,<1.35.0)"] +ssm-sap = ["mypy-boto3-ssm-sap (>=1.34.0,<1.35.0)"] +sso = ["mypy-boto3-sso (>=1.34.0,<1.35.0)"] +sso-admin = ["mypy-boto3-sso-admin (>=1.34.0,<1.35.0)"] +sso-oidc = ["mypy-boto3-sso-oidc (>=1.34.0,<1.35.0)"] +stepfunctions = ["mypy-boto3-stepfunctions (>=1.34.0,<1.35.0)"] +storagegateway = ["mypy-boto3-storagegateway (>=1.34.0,<1.35.0)"] +sts = ["mypy-boto3-sts (>=1.34.0,<1.35.0)"] +supplychain = ["mypy-boto3-supplychain (>=1.34.0,<1.35.0)"] +support = ["mypy-boto3-support (>=1.34.0,<1.35.0)"] +support-app = ["mypy-boto3-support-app (>=1.34.0,<1.35.0)"] +swf = ["mypy-boto3-swf (>=1.34.0,<1.35.0)"] +synthetics = ["mypy-boto3-synthetics (>=1.34.0,<1.35.0)"] +textract = ["mypy-boto3-textract (>=1.34.0,<1.35.0)"] +timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.34.0,<1.35.0)"] +timestream-query = ["mypy-boto3-timestream-query (>=1.34.0,<1.35.0)"] +timestream-write = ["mypy-boto3-timestream-write (>=1.34.0,<1.35.0)"] +tnb = ["mypy-boto3-tnb (>=1.34.0,<1.35.0)"] +transcribe = ["mypy-boto3-transcribe (>=1.34.0,<1.35.0)"] +transfer = ["mypy-boto3-transfer (>=1.34.0,<1.35.0)"] +translate = ["mypy-boto3-translate (>=1.34.0,<1.35.0)"] +trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.34.0,<1.35.0)"] +verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.34.0,<1.35.0)"] +voice-id = ["mypy-boto3-voice-id (>=1.34.0,<1.35.0)"] +vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.34.0,<1.35.0)"] +waf = ["mypy-boto3-waf (>=1.34.0,<1.35.0)"] +waf-regional = ["mypy-boto3-waf-regional (>=1.34.0,<1.35.0)"] +wafv2 = ["mypy-boto3-wafv2 (>=1.34.0,<1.35.0)"] +wellarchitected = ["mypy-boto3-wellarchitected (>=1.34.0,<1.35.0)"] +wisdom = ["mypy-boto3-wisdom (>=1.34.0,<1.35.0)"] +workdocs = ["mypy-boto3-workdocs (>=1.34.0,<1.35.0)"] +worklink = ["mypy-boto3-worklink (>=1.34.0,<1.35.0)"] +workmail = ["mypy-boto3-workmail (>=1.34.0,<1.35.0)"] +workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.34.0,<1.35.0)"] +workspaces = ["mypy-boto3-workspaces (>=1.34.0,<1.35.0)"] +workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.34.0,<1.35.0)"] +workspaces-web = ["mypy-boto3-workspaces-web (>=1.34.0,<1.35.0)"] +xray = ["mypy-boto3-xray (>=1.34.0,<1.35.0)"] + [[package]] name = "botocore" version = "1.34.103" @@ -492,6 +883,23 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.20.9)"] +[[package]] +name = "botocore-stubs" +version = "1.34.94" +description = "Type annotations and code completion for botocore" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "botocore_stubs-1.34.94-py3-none-any.whl", hash = "sha256:b0345f55babd8b901c53804fc5c326a4a0bd2e23e3b71f9ea5d9f7663466e6ba"}, + {file = "botocore_stubs-1.34.94.tar.gz", hash = "sha256:64d80a3467e3b19939e9c2750af33328b3087f8f524998dbdf7ed168227f507d"}, +] + +[package.dependencies] +types-awscrt = "*" + +[package.extras] +botocore = ["botocore"] + [[package]] name = "bs4" version = "0.0.1" @@ -969,6 +1377,73 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pill test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +[[package]] +name = "coverage" +version = "7.5.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, + {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, + {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, + {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, + {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, + {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, + {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "curl-cffi" version = "0.6.2" @@ -1216,19 +1691,19 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "6.1.0" +version = "7.0.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" +pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "flatbuffers" @@ -1472,6 +1947,14 @@ files = [ [package.dependencies] google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = [ + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, +] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -1536,6 +2019,38 @@ files = [ google-auth = "*" httplib2 = ">=0.19.0" +[[package]] +name = "google-cloud-appengine-logging" +version = "1.4.3" +description = "Google Cloud Appengine Logging API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-appengine-logging-1.4.3.tar.gz", hash = "sha256:fb504e6199fe8de85baa9d31cecf6776877851fe58867de603317ec7cc739987"}, + {file = "google_cloud_appengine_logging-1.4.3-py2.py3-none-any.whl", hash = "sha256:8e30af51d853f219caf29e8b8b342b9ce8214b29f334dafae38d39aaaff7d372"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-audit-log" +version = "0.2.5" +description = "Google Cloud Audit Protos" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-audit-log-0.2.5.tar.gz", hash = "sha256:86e2faba3383adc8fd04a5bd7fd4f960b3e4aedaa7ed950f2f891ce16902eb6b"}, + {file = "google_cloud_audit_log-0.2.5-py2.py3-none-any.whl", hash = "sha256:18b94d4579002a450b7902cd2e8b8fdcb1ea2dd4df3b41f8f82be6d9f7fcd746"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.56.2,<2.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + [[package]] name = "google-cloud-core" version = "2.4.1" @@ -1554,6 +2069,30 @@ google-auth = ">=1.25.0,<3.0dev" [package.extras] grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] +[[package]] +name = "google-cloud-logging" +version = "3.10.0" +description = "Stackdriver Logging API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-logging-3.10.0.tar.gz", hash = "sha256:d93d347351240ddb14cfe201987a2d32cf9d7f478b8b2fabed3015b425b3274f"}, + {file = "google_cloud_logging-3.10.0-py2.py3-none-any.whl", hash = "sha256:132192beb45731130a2ffbcd4b2b5cbd87370e7dcfa7397ae4002154f542bd20"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" +google-cloud-appengine-logging = ">=0.1.0,<2.0.0dev" +google-cloud-audit-log = ">=0.1.0,<1.0.0dev" +google-cloud-core = ">=2.0.0,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = [ + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + [[package]] name = "google-cloud-storage" version = "2.14.0" @@ -1686,6 +2225,7 @@ files = [ ] [package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" [package.extras] @@ -1762,6 +2302,41 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "groq" +version = "0.8.0" +description = "The official Python library for the groq API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "groq-0.8.0-py3-none-any.whl", hash = "sha256:f5e4e892d45001241a930db451e633ca1f0007e3f749deaa5d7360062fcd61e3"}, + {file = "groq-0.8.0.tar.gz", hash = "sha256:37ceb2f706bd516d0bfcac8e89048a24b375172987a0d6bd9efb521c54f6deff"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +typing-extensions = ">=4.7,<5" + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + [[package]] name = "grpcio" version = "1.60.0" @@ -1828,6 +2403,41 @@ files = [ [package.extras] protobuf = ["grpcio-tools (>=1.60.0)"] +[[package]] +name = "grpcio-status" +version = "1.60.0" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.60.0.tar.gz", hash = "sha256:f10e0b6db3adc0fdc244b71962814ee982996ef06186446b5695b9fa635aa1ab"}, + {file = "grpcio_status-1.60.0-py3-none-any.whl", hash = "sha256:7d383fa36e59c1e61d380d91350badd4d12ac56e4de2c2b831b050362c3c572e"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.60.0" +protobuf = ">=4.21.6" + +[[package]] +name = "gtts" +version = "2.5.1" +description = "gTTS (Google Text-to-Speech), a Python library and CLI tool to interface with Google Translate text-to-speech API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gTTS-2.5.1-py3-none-any.whl", hash = "sha256:273ec8a5077b25e60ca5a266ed254b54d1f14032b0af3ba00092d14966148664"}, + {file = "gTTS-2.5.1.tar.gz", hash = "sha256:02d0a9874f945dee9cd5092991c60bc88d4b7767b8cd81144b6fb49dc3de6897"}, +] + +[package.dependencies] +click = ">=7.1,<8.2" +requests = ">=2.27,<3" + +[package.extras] +docs = ["sphinx", "sphinx-autobuild", "sphinx-click", "sphinx-mdinclude", "sphinx-rtd-theme"] +tests = ["pytest (>=7.1.3,<8.1.0)", "pytest-cov", "testfixtures"] + [[package]] name = "h11" version = "0.14.0" @@ -3010,51 +3620,18 @@ files = [ ] [[package]] -name = "mypy" -version = "1.8.0" -description = "Optional static typing for Python" +name = "mypy-boto3-s3" +version = "1.34.105" +description = "Type annotations for boto3.S3 1.34.105 service generated with mypy-boto3-builder 7.24.0" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy_boto3_s3-1.34.105-py3-none-any.whl", hash = "sha256:95fbc6bcba2bb03c20a97cc5cf60ff66c6842c8c4fc4183c49bfa35905d5a1ee"}, + {file = "mypy_boto3_s3-1.34.105.tar.gz", hash = "sha256:a137bca9bbe86c0fe35bbf36a2d44ab62526f41bb683550dd6cfbb5a10ede832"}, ] [package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} [[package]] name = "mypy-extensions" @@ -3463,9 +4040,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -3761,6 +4338,23 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + [[package]] name = "protobuf" version = "4.25.2" @@ -3992,13 +4586,13 @@ files = [ [[package]] name = "pyflakes" -version = "3.1.0" +version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] @@ -4093,6 +4687,24 @@ files = [ {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, ] +[[package]] +name = "pyright" +version = "1.1.364" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.364-py3-none-any.whl", hash = "sha256:865f1e02873c5dc7427c95acf53659a118574010e6fb364e27e47ec5c46a9f26"}, + {file = "pyright-1.1.364.tar.gz", hash = "sha256:612a2106a4078ec57efc22b5620729e9bdf4a3c17caba013b534bd33f7d08e5a"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" + +[package.extras] +all = ["twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] + [[package]] name = "pysocks" version = "1.7.1" @@ -4145,6 +4757,24 @@ pytest = ">=7.0.0" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -4277,7 +4907,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -5168,47 +5797,47 @@ torch = ["torch (>=1.6.0)"] [[package]] name = "tiktoken" -version = "0.5.2" +version = "0.7.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.8" files = [ - {file = "tiktoken-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c4e654282ef05ec1bd06ead22141a9a1687991cef2c6a81bdd1284301abc71d"}, - {file = "tiktoken-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b3134aa24319f42c27718c6967f3c1916a38a715a0fa73d33717ba121231307"}, - {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6092e6e77730929c8c6a51bb0d7cfdf1b72b63c4d033d6258d1f2ee81052e9e5"}, - {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ad8ae2a747622efae75837abba59be6c15a8f31b4ac3c6156bc56ec7a8e631"}, - {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51cba7c8711afa0b885445f0637f0fcc366740798c40b981f08c5f984e02c9d1"}, - {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3d8c7d2c9313f8e92e987d585ee2ba0f7c40a0de84f4805b093b634f792124f5"}, - {file = "tiktoken-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:692eca18c5fd8d1e0dde767f895c17686faaa102f37640e884eecb6854e7cca7"}, - {file = "tiktoken-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:138d173abbf1ec75863ad68ca289d4da30caa3245f3c8d4bfb274c4d629a2f77"}, - {file = "tiktoken-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7388fdd684690973fdc450b47dfd24d7f0cbe658f58a576169baef5ae4658607"}, - {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a114391790113bcff670c70c24e166a841f7ea8f47ee2fe0e71e08b49d0bf2d4"}, - {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca96f001e69f6859dd52926d950cfcc610480e920e576183497ab954e645e6ac"}, - {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:15fed1dd88e30dfadcdd8e53a8927f04e1f6f81ad08a5ca824858a593ab476c7"}, - {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f8e692db5756f7ea8cb0cfca34638316dcf0841fb8469de8ed7f6a015ba0b0"}, - {file = "tiktoken-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:bcae1c4c92df2ffc4fe9f475bf8148dbb0ee2404743168bbeb9dcc4b79dc1fdd"}, - {file = "tiktoken-0.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b76a1e17d4eb4357d00f0622d9a48ffbb23401dcf36f9716d9bd9c8e79d421aa"}, - {file = "tiktoken-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01d8b171bb5df4035580bc26d4f5339a6fd58d06f069091899d4a798ea279d3e"}, - {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42adf7d4fb1ed8de6e0ff2e794a6a15005f056a0d83d22d1d6755a39bffd9e7f"}, - {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3f894dbe0adb44609f3d532b8ea10820d61fdcb288b325a458dfc60fefb7db"}, - {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58ccfddb4e62f0df974e8f7e34a667981d9bb553a811256e617731bf1d007d19"}, - {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58902a8bad2de4268c2a701f1c844d22bfa3cbcc485b10e8e3e28a050179330b"}, - {file = "tiktoken-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e39257826d0647fcac403d8fa0a474b30d02ec8ffc012cfaf13083e9b5e82c5"}, - {file = "tiktoken-0.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bde3b0fbf09a23072d39c1ede0e0821f759b4fa254a5f00078909158e90ae1f"}, - {file = "tiktoken-0.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ddee082dcf1231ccf3a591d234935e6acf3e82ee28521fe99af9630bc8d2a60"}, - {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35c057a6a4e777b5966a7540481a75a31429fc1cb4c9da87b71c8b75b5143037"}, - {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c4a049b87e28f1dc60509f8eb7790bc8d11f9a70d99b9dd18dfdd81a084ffe6"}, - {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5bf5ce759089f4f6521ea6ed89d8f988f7b396e9f4afb503b945f5c949c6bec2"}, - {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c964f554af1a96884e01188f480dad3fc224c4bbcf7af75d4b74c4b74ae0125"}, - {file = "tiktoken-0.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:368dd5726d2e8788e47ea04f32e20f72a2012a8a67af5b0b003d1e059f1d30a3"}, - {file = "tiktoken-0.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2deef9115b8cd55536c0a02c0203512f8deb2447f41585e6d929a0b878a0dd2"}, - {file = "tiktoken-0.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ed7d380195affbf886e2f8b92b14edfe13f4768ff5fc8de315adba5b773815e"}, - {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76fce01309c8140ffe15eb34ded2bb94789614b7d1d09e206838fc173776a18"}, - {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60a5654d6a2e2d152637dd9a880b4482267dfc8a86ccf3ab1cec31a8c76bfae8"}, - {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41d4d3228e051b779245a8ddd21d4336f8975563e92375662f42d05a19bdff41"}, - {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c1cdec2c92fcde8c17a50814b525ae6a88e8e5b02030dc120b76e11db93f13"}, - {file = "tiktoken-0.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:84ddb36faedb448a50b246e13d1b6ee3437f60b7169b723a4b2abad75e914f3e"}, - {file = "tiktoken-0.5.2.tar.gz", hash = "sha256:f54c581f134a8ea96ce2023ab221d4d4d81ab614efa0b2fbce926387deb56c80"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5"}, + {file = "tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89"}, + {file = "tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97"}, + {file = "tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2398fecd38c921bcd68418675a6d155fad5f5e14c2e92fcf5fe566fa5485a858"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f5f6afb52fb8a7ea1c811e435e4188f2bef81b5e0f7a8635cc79b0eef0193d6"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861f9ee616766d736be4147abac500732b505bf7013cfaf019b85892637f235e"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54031f95c6939f6b78122c0aa03a93273a96365103793a22e1793ee86da31685"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fffdcb319b614cf14f04d02a52e26b1d1ae14a570f90e9b55461a72672f7b13d"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c72baaeaefa03ff9ba9688624143c858d1f6b755bb85d456d59e529e17234769"}, + {file = "tiktoken-0.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:131b8aeb043a8f112aad9f46011dced25d62629091e51d9dc1adbf4a1cc6aa98"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cabc6dc77460df44ec5b879e68692c63551ae4fae7460dd4ff17181df75f1db7"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d57f29171255f74c0aeacd0651e29aa47dff6f070cb9f35ebc14c82278f3b25"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ee92776fdbb3efa02a83f968c19d4997a55c8e9ce7be821ceee04a1d1ee149c"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e215292e99cb41fbc96988ef62ea63bb0ce1e15f2c147a61acc319f8b4cbe5bf"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a81bac94769cab437dd3ab0b8a4bc4e0f9cf6835bcaa88de71f39af1791727a"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6d73ea93e91d5ca771256dfc9d1d29f5a554b83821a1dc0891987636e0ae226"}, + {file = "tiktoken-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:2bcb28ddf79ffa424f171dfeef9a4daff61a94c631ca6813f43967cb263b83b9"}, + {file = "tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6"}, ] [package.dependencies] @@ -5447,6 +6076,17 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2 doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +[[package]] +name = "types-awscrt" +version = "0.20.9" +description = "Type annotations and code completion for awscrt" +optional = false +python-versions = "<4.0,>=3.7" +files = [ + {file = "types_awscrt-0.20.9-py3-none-any.whl", hash = "sha256:3ae374b553e7228ba41a528cf42bd0b2ad7303d806c73eff4aaaac1515e3ea4e"}, + {file = "types_awscrt-0.20.9.tar.gz", hash = "sha256:64898a2f4a2468f66233cb8c29c5f66de907cf80ba1ef5bb1359aef2f81bb521"}, +] + [[package]] name = "types-requests" version = "2.31.0.6" @@ -5461,6 +6101,17 @@ files = [ [package.dependencies] types-urllib3 = "*" +[[package]] +name = "types-s3transfer" +version = "0.10.1" +description = "Type annotations and code completion for s3transfer" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "types_s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:49a7c81fa609ac1532f8de3756e64b58afcecad8767933310228002ec7adff74"}, + {file = "types_s3transfer-0.10.1.tar.gz", hash = "sha256:02154cce46528287ad76ad1a0153840e0492239a0887e8833466eccf84b98da0"}, +] + [[package]] name = "types-urllib3" version = "1.26.25.14" @@ -5630,38 +6281,40 @@ colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\" and python [[package]] name = "watchdog" -version = "3.0.0" +version = "4.0.0" description = "Filesystem events monitoring" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, - {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, - {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, - {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, - {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, - {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, - {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, - {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, - {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, - {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, - {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, - {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, - {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, - {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, - {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, - {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, - {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, - {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, - {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, + {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, + {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, + {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, + {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, + {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, + {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, + {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, ] [package.extras] @@ -6117,4 +6770,4 @@ benchmark = ["agbenchmark"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "d88392928c5d8009bd9eafd9d2ccd8c810e63928c780cfa9d88e2ce3ff659c1f" +content-hash = "e04a03e3f2663d3e54d5f4e6649cd4442cb00fd17ae5c68d06dbaadfc02ca309" diff --git a/autogpts/forge/pyproject.toml b/forge/pyproject.toml similarity index 69% rename from autogpts/forge/pyproject.toml rename to forge/pyproject.toml index 1c8796943d..e99a2300dd 100644 --- a/autogpts/forge/pyproject.toml +++ b/forge/pyproject.toml @@ -9,7 +9,7 @@ packages = [{ include = "forge" }] [tool.poetry.dependencies] python = "^3.10" -agbenchmark = { path = "../../benchmark", optional = true } +agbenchmark = { path = "../benchmark", optional = true } # agbenchmark = {git = "https://github.com/Significant-Gravitas/AutoGPT.git", subdirectory = "benchmark", optional = true} aiohttp = "^3.8.5" anthropic = "^0.25.1" @@ -26,7 +26,10 @@ duckduckgo-search = "^5.0.0" fastapi = "^0.109.1" gitpython = "^3.1.32" google-api-python-client = "*" +google-cloud-logging = "^3.8.0" google-cloud-storage = "^2.13.0" +groq = "^0.8.0" +gTTS = "^2.3.1" jinja2 = "^3.1.2" jsonschema = "*" litellm = "^1.17.9" @@ -46,26 +49,27 @@ sqlalchemy = "^2.0.19" sentry-sdk = "^1.40.4" spacy = "^3.0.0" tenacity = "^8.2.2" -tiktoken = "^0.5.0" +tiktoken = ">=0.7.0,<1.0.0" toml = "^0.10.2" uvicorn = "^0.23.2" +watchdog = "4.0.0" webdriver-manager = "^4.0.1" [tool.poetry.extras] benchmark = ["agbenchmark"] [tool.poetry.group.dev.dependencies] -isort = "^5.12.0" -black = "^23.3.0" +black = "^23.12.1" +flake8 = "^7.0.0" +isort = "^5.13.1" +pyright = "^1.1.364" pre-commit = "^3.3.3" -mypy = "^1.4.1" -flake8 = "^6.0.0" +boto3-stubs = { extras = ["s3"], version = "^1.33.6" } types-requests = "^2.31.0.2" pytest = "^7.4.0" pytest-asyncio = "^0.21.1" -watchdog = "^3.0.0" +pytest-cov = "^5.0.0" mock = "^5.1.0" -autoflake = "^2.2.0" pydevd-pycharm = "^233.6745.319" @@ -73,20 +77,21 @@ pydevd-pycharm = "^233.6745.319" requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + [tool.black] line-length = 88 target-version = ['py310'] include = '\.pyi?$' -packages = ["forge"] -extend-exclude = '(/dist|/.venv|/venv|/build|/agent|agbenchmark/challenges)/' + [tool.isort] profile = "black" -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -ensure_newline_before_comments = true -line_length = 88 -sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] -skip_glob = [".tox", "__pycache__", "*.pyc", "venv*/*", "reports", "venv", "env", "node_modules", ".env", ".venv", "dist", "agent/*", "agbenchmark/challenges/*"] + + +[tool.pyright] +pythonVersion = "3.10" + + +[tool.pytest.ini_options] +pythonpath = ["forge"] +testpaths = ["forge", "tests"] diff --git a/autogpts/forge/run b/forge/run similarity index 100% rename from autogpts/forge/run rename to forge/run diff --git a/autogpts/forge/run_benchmark b/forge/run_benchmark similarity index 100% rename from autogpts/forge/run_benchmark rename to forge/run_benchmark diff --git a/autogpts/forge/setup b/forge/setup similarity index 100% rename from autogpts/forge/setup rename to forge/setup diff --git a/autogpts/forge/tutorials/001_getting_started.md b/forge/tutorials/001_getting_started.md similarity index 98% rename from autogpts/forge/tutorials/001_getting_started.md rename to forge/tutorials/001_getting_started.md index 4d9f86e550..56cd70a71a 100644 --- a/autogpts/forge/tutorials/001_getting_started.md +++ b/forge/tutorials/001_getting_started.md @@ -84,7 +84,7 @@ This command forcefully stops the agent. You can also restart it using the start ## To Recap - We've forked the AutoGPT repo and cloned it locally on your machine. - we connected the library with our personal github access token as part of the setup. -- We've run the agent and it's tasking server successfully without an error. +- We've run the agent and its tasking server successfully without an error. - We've logged into the server site at localhost:8000 using our github account. Make sure you've completed every step successfully before moving on :). diff --git a/autogpts/forge/tutorials/002_blueprint_of_an_agent.md b/forge/tutorials/002_blueprint_of_an_agent.md similarity index 98% rename from autogpts/forge/tutorials/002_blueprint_of_an_agent.md rename to forge/tutorials/002_blueprint_of_an_agent.md index a84bf79c2c..15e192651a 100644 --- a/autogpts/forge/tutorials/002_blueprint_of_an_agent.md +++ b/forge/tutorials/002_blueprint_of_an_agent.md @@ -34,7 +34,7 @@ Anatomy of an Agent from the Agent Landscape Survey ### **Profile** Humans naturally adapt our mindset based on the tasks we're tackling, whether it's writing, cooking, or playing sports. Similarly, agents can be conditioned or "profiled" to specialize in specific tasks. -The profile of an agent is it's personality, mindset, and high-level instructions. Research indicates that merely informing an agent that it's an expert in a certain domain can boost its performance. +The profile of an agent is its personality, mindset, and high-level instructions. Research indicates that merely informing an agent that it's an expert in a certain domain can boost its performance. | **Potential Applications of Profiling** | **Description** | |-----------------------------------------|----------------------------------------------------------------------------------------------------------| diff --git a/autogpts/forge/tutorials/003_crafting_agent_logic.md b/forge/tutorials/003_crafting_agent_logic.md similarity index 100% rename from autogpts/forge/tutorials/003_crafting_agent_logic.md rename to forge/tutorials/003_crafting_agent_logic.md diff --git a/autogpts/forge/tutorials/004_memories.md b/forge/tutorials/004_memories.md similarity index 100% rename from autogpts/forge/tutorials/004_memories.md rename to forge/tutorials/004_memories.md diff --git a/frontend/lib/services/task_service.dart b/frontend/lib/services/task_service.dart index bb55834865..6c7fe24e62 100644 --- a/frontend/lib/services/task_service.dart +++ b/frontend/lib/services/task_service.dart @@ -40,7 +40,7 @@ class TaskService { } /// Fetches all tasks across all pages. - // TODO: Temporaily make page size 10000 until pagination is fixed + // TODO: Temporarily make page size 10000 until pagination is fixed Future> fetchAllTasks({int pageSize = 10000}) async { int currentPage = 1; List allTasks = []; diff --git a/rnd/autogpt_server/README.md b/rnd/autogpt_server/README.md new file mode 100644 index 0000000000..1563240a32 --- /dev/null +++ b/rnd/autogpt_server/README.md @@ -0,0 +1,20 @@ +# Next Gen AutoGPT + +This is a research project into creating the next generation of autogpt, which is an autogpt agent server. + +The agent server will enable the creation of composite multi-agent system that utilize AutoGPT Agent as its default agent. + + +## Project Outline + +Currently the project mainly consist of these components: + +*agent_api* +A component that will expose API endpoints for the creation & execution of agents. +This component will make connections to the database to persist and read the agents. +It will also trigger the agent execution by pushing its execution request to the ExecutionQueue. + +*agent_executor* +A component that will execute the agents. +This component will be a pool of processes/threads that will consume the ExecutionQueue and execute the agent accordingly. +The result and progress of its execution will be persisted in the database. diff --git a/rnd/autogpt_server/autogpt_server/agent_api/__init__.py b/rnd/autogpt_server/autogpt_server/agent_api/__init__.py new file mode 100644 index 0000000000..1ea8dc278e --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/agent_api/__init__.py @@ -0,0 +1 @@ +from .server import start_server # noqa diff --git a/rnd/autogpt_server/autogpt_server/agent_api/server.py b/rnd/autogpt_server/autogpt_server/agent_api/server.py new file mode 100644 index 0000000000..cbbdb018cd --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/agent_api/server.py @@ -0,0 +1,39 @@ +import uvicorn +from fastapi import FastAPI, APIRouter + +from autogpt_server.data import ExecutionQueue + + +class AgentServer: + + def __init__(self, queue: ExecutionQueue): + self.app = FastAPI( + title="AutoGPT Agent Server", + description=( + "This server is used to execute agents that are created by the " + "AutoGPT system." + ), + summary="AutoGPT Agent Server", + version="0.1", + ) + self.execution_queue = queue + + # Define the API routes + self.router = APIRouter() + self.router.add_api_route( + path="/agents/{agent_id}/execute", + endpoint=self.execute_agent, + methods=["POST"], + ) + self.app.include_router(self.router) + + def execute_agent(self, agent_id: str): + execution_id = self.execution_queue.add(agent_id) + return {"execution_id": execution_id, "agent_id": agent_id} + + +def start_server(queue: ExecutionQueue, use_uvicorn: bool = True): + app = AgentServer(queue).app + if use_uvicorn: + uvicorn.run(app) + return app diff --git a/rnd/autogpt_server/autogpt_server/agent_executor/__init__.py b/rnd/autogpt_server/autogpt_server/agent_executor/__init__.py new file mode 100644 index 0000000000..e79245e20f --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/agent_executor/__init__.py @@ -0,0 +1 @@ +from .executor import start_executors # noqa diff --git a/rnd/autogpt_server/autogpt_server/agent_executor/executor.py b/rnd/autogpt_server/autogpt_server/agent_executor/executor.py new file mode 100644 index 0000000000..d5daed5ee7 --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/agent_executor/executor.py @@ -0,0 +1,42 @@ +import logging +import time +from concurrent.futures import ThreadPoolExecutor +from multiprocessing import Process + +from autogpt_server.data import ExecutionQueue + +logger = logging.getLogger(__name__) + + +class AgentExecutor: + # TODO: Replace this by an actual Agent Execution. + def __execute(id: str, data: str) -> None: + logger.warning(f"Executor processing started, execution_id: {id}, data: {data}") + for i in range(5): + logger.warning( + f"Executor processing step {i}, execution_id: {id}, data: {data}" + ) + time.sleep(1) + logger.warning( + f"Executor processing completed, execution_id: {id}, data: {data}" + ) + + def start_executor(pool_size: int, queue: ExecutionQueue) -> None: + with ThreadPoolExecutor(max_workers=pool_size) as executor: + while True: + execution = queue.get() + if not execution: + time.sleep(1) + continue + executor.submit( + AgentExecutor.__execute, + execution.execution_id, + execution.data, + ) + + +def start_executors(pool_size: int, queue: ExecutionQueue) -> None: + executor_process = Process( + target=AgentExecutor.start_executor, args=(pool_size, queue) + ) + executor_process.start() diff --git a/rnd/autogpt_server/autogpt_server/app.py b/rnd/autogpt_server/autogpt_server/app.py new file mode 100644 index 0000000000..5b8bf81d7b --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/app.py @@ -0,0 +1,13 @@ +from autogpt_server.agent_api import start_server +from autogpt_server.agent_executor import start_executors +from autogpt_server.data import ExecutionQueue + + +def main() -> None: + queue = ExecutionQueue() + start_executors(5, queue) + start_server(queue) + + +if __name__ == "__main__": + main() diff --git a/rnd/autogpt_server/autogpt_server/data.py b/rnd/autogpt_server/autogpt_server/data.py new file mode 100644 index 0000000000..5fbc184c09 --- /dev/null +++ b/rnd/autogpt_server/autogpt_server/data.py @@ -0,0 +1,36 @@ +import uuid +from multiprocessing import Queue + + +class Execution: + """Data model for an execution of an Agent""" + + def __init__(self, execution_id: str, data: str): + self.execution_id = execution_id + self.data = data + + +# TODO: This shared class make api & executor coupled in one machine. +# Replace this with a persistent & remote-hosted queue. +# One very likely candidate would be persisted Redis (Redis Queue). +# It will also open the possibility of using it for other purposes like +# caching, execution engine broker (like Celery), user session management etc. +class ExecutionQueue: + """ + Queue for managing the execution of agents. + This will be shared between different processes + """ + + def __init__(self): + self.queue = Queue() + + def add(self, data: str) -> str: + execution_id = uuid.uuid4() + self.queue.put(Execution(str(execution_id), data)) + return str(execution_id) + + def get(self) -> Execution | None: + return self.queue.get() + + def empty(self) -> bool: + return self.queue.empty() diff --git a/rnd/autogpt_server/poetry.lock b/rnd/autogpt_server/poetry.lock new file mode 100644 index 0000000000..bf8dd89853 --- /dev/null +++ b/rnd/autogpt_server/poetry.lock @@ -0,0 +1,1151 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "certifi" +version = "2024.6.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.111.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, +] + +[package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.4" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, +] + +[package.dependencies] +typer = ">=0.12.3" + +[package.extras] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "orjson" +version = "3.10.3" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, + {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, + {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, + {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, + {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, + {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, + {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, + {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, + {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, + {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, + {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, + {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, + {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, + {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, + {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, + {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.7.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.2.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + +[[package]] +name = "uvicorn" +version = "0.30.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "0.22.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "c6f5c72564120998be36fde13fbec725f4dc87c04795a8ebf481138e62b0129e" diff --git a/rnd/autogpt_server/pyproject.toml b/rnd/autogpt_server/pyproject.toml new file mode 100644 index 0000000000..ced6a692a8 --- /dev/null +++ b/rnd/autogpt_server/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "autogpt_server" +version = "0.1.0" +description = "" +authors = ["SwiftyOS "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +click = "^8.1.7" +pydantic = "^2.7.1" +pytest = "^8.2.1" +uvicorn = "^0.30.1" +fastapi = "^0.111.0" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +app = "autogpt_server.app:main" diff --git a/rnd/autogpt_server/test/test_app.py b/rnd/autogpt_server/test/test_app.py new file mode 100644 index 0000000000..2707d07668 --- /dev/null +++ b/rnd/autogpt_server/test/test_app.py @@ -0,0 +1,30 @@ +import pytest + +from autogpt_server.data import ExecutionQueue +from autogpt_server.agent_api import start_server +from autogpt_server.agent_executor import start_executors +from fastapi.testclient import TestClient + + +@pytest.fixture +def client(): + execution_queue = ExecutionQueue() + start_executors(5, execution_queue) + return TestClient(start_server(execution_queue, use_uvicorn=False)) + + +def test_execute_agent(client): + # Assert API is working + response = client.post("/agents/dummy_agent_1/execute") + assert response.status_code == 200 + + # Assert response is correct + data = response.json() + exec_id = data["execution_id"] + agent_id = data["agent_id"] + assert agent_id == "dummy_agent_1" + assert isinstance(exec_id, str) + assert len(exec_id) == 36 + + # TODO: Add assertion that the executor is executed after some time + # Add this when db integration is done. diff --git a/rnd/nextgenautogpt/README.md b/rnd/nextgenautogpt/README.md new file mode 100644 index 0000000000..5104b2f44b --- /dev/null +++ b/rnd/nextgenautogpt/README.md @@ -0,0 +1,27 @@ +# Next Gen AutoGPT + +This is a research project into creating the next generation of autogpt, which is an autogpt agent server. + +It will come with the AutoGPT Agent as the default agent + + +## Project Outline + +``` +. +├── READEME.md +├── nextgenautogpt +│ ├── __init__.py +│ ├── __main__.py +│ ├── cli.py # The CLI tool for running the system +│ ├── executor # The Component Executor Process +│ │ └── __init__.py +│ ├── manager # The Agent Manager it manages a pool of executors and schedules components to run +│ │ └── __init__.py +│ └── server # The main application. It includes the api server and additional modules +│ └── __init__.py +└── pyproject.toml +``` + + + diff --git a/autogpts/autogpt/tests/unit/__init__.py b/rnd/nextgenautogpt/nextgenautogpt/__init__.py similarity index 100% rename from autogpts/autogpt/tests/unit/__init__.py rename to rnd/nextgenautogpt/nextgenautogpt/__init__.py diff --git a/rnd/nextgenautogpt/nextgenautogpt/__main__.py b/rnd/nextgenautogpt/nextgenautogpt/__main__.py new file mode 100644 index 0000000000..d529530585 --- /dev/null +++ b/rnd/nextgenautogpt/nextgenautogpt/__main__.py @@ -0,0 +1,39 @@ +import multiprocessing as mp +from typing import Any + +import nextgenautogpt.manager.manager as mod_manager +import nextgenautogpt.server.server as mod_server + + +def main() -> None: + # Create queues/pipes for communication + server_to_manager: mp.Queue[Any] = mp.Queue() + manager_to_server: mp.Queue[Any] = mp.Queue() + + # Create and start server process + server: mp.Process = mp.Process( + target=mod_server.run_server, + args=( + server_to_manager, + manager_to_server, + ), + ) + server.start() + + # Create and start manager process + manager: mp.Process = mp.Process( + target=mod_manager.run_manager, + args=( + server_to_manager, + manager_to_server, + ), + ) + manager.start() + + server.join() + manager.join() + + +if __name__ == "__main__": + mp.set_start_method("spawn") + main() diff --git a/autogpts/forge/__init__.py b/rnd/nextgenautogpt/nextgenautogpt/cli.py similarity index 100% rename from autogpts/forge/__init__.py rename to rnd/nextgenautogpt/nextgenautogpt/cli.py diff --git a/autogpts/forge/forge/__init__.py b/rnd/nextgenautogpt/nextgenautogpt/executor/__init__.py similarity index 100% rename from autogpts/forge/forge/__init__.py rename to rnd/nextgenautogpt/nextgenautogpt/executor/__init__.py diff --git a/rnd/nextgenautogpt/nextgenautogpt/executor/executor.py b/rnd/nextgenautogpt/nextgenautogpt/executor/executor.py new file mode 100644 index 0000000000..73c63bb56b --- /dev/null +++ b/rnd/nextgenautogpt/nextgenautogpt/executor/executor.py @@ -0,0 +1,15 @@ +import multiprocessing as mp +import time +from typing import Any + + +def run_executor(manager_to_executor: mp.Queue, executors_to_manager: mp.Queue) -> None: + # Each executor process will run this initializer + print("Executor process started") + while True: + if not manager_to_executor.empty(): + task = manager_to_executor.get() + print(f"Executor processing: {task}") + executors_to_manager.put("Task completed") + # Simulate executor work + time.sleep(1) diff --git a/autogpts/forge/forge/content_processing/__init__.py b/rnd/nextgenautogpt/nextgenautogpt/manager/__init__.py similarity index 100% rename from autogpts/forge/forge/content_processing/__init__.py rename to rnd/nextgenautogpt/nextgenautogpt/manager/__init__.py diff --git a/rnd/nextgenautogpt/nextgenautogpt/manager/manager.py b/rnd/nextgenautogpt/nextgenautogpt/manager/manager.py new file mode 100644 index 0000000000..e18326e8cc --- /dev/null +++ b/rnd/nextgenautogpt/nextgenautogpt/manager/manager.py @@ -0,0 +1,33 @@ +import multiprocessing as mp +import time + +import nextgenautogpt.executor.executor as mod_executor + + +def run_manager(server_to_manager: mp.Queue, manager_to_server: mp.Queue) -> None: + # Create queue for communication between manager and executors + print("Manager process started") + manager_to_executor = mp.Queue() + executors_to_manager = mp.Queue() + # Create and start a pool of executor processes + with mp.Pool( + processes=5, + initializer=mod_executor.run_executor, + initargs=( + manager_to_executor, + executors_to_manager, + ), + ): + while True: + if not server_to_manager.empty(): + message = server_to_manager.get() + print(f"Manager received: {message}") + manager_to_server.put("Manager: Received message from server") + manager_to_executor.put("Task for executor") + # Simulate manager work + time.sleep(1) + if not executors_to_manager.empty(): + message = executors_to_manager.get() + print(f"Manager received: {message}") + # Simulate manager work + time.sleep(1) diff --git a/autogpts/forge/forge/llm/__init__.py b/rnd/nextgenautogpt/nextgenautogpt/server/__init__.py similarity index 100% rename from autogpts/forge/forge/llm/__init__.py rename to rnd/nextgenautogpt/nextgenautogpt/server/__init__.py diff --git a/rnd/nextgenautogpt/nextgenautogpt/server/server.py b/rnd/nextgenautogpt/nextgenautogpt/server/server.py new file mode 100644 index 0000000000..23ead8ea2c --- /dev/null +++ b/rnd/nextgenautogpt/nextgenautogpt/server/server.py @@ -0,0 +1,17 @@ +import multiprocessing as mp +import time +from typing import Any + + +def run_server(server_to_manager: mp.Queue, manager_to_server: mp.Queue) -> None: + print("Server process started") + while True: + message = "Message from server" + server_to_manager.put(message) + # Simulate server work + time.sleep(1) + if not manager_to_server.empty(): + message = manager_to_server.get() + print(f"Server received: {message}") + # Simulate server work + time.sleep(1) diff --git a/rnd/nextgenautogpt/poetry.lock b/rnd/nextgenautogpt/poetry.lock new file mode 100644 index 0000000000..c945aaf9c3 --- /dev/null +++ b/rnd/nextgenautogpt/poetry.lock @@ -0,0 +1,163 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "pydantic" +version = "2.7.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "37f738f9702fe6c311bc32b3ccbd182c35a6a612c5866def9e8fc14dfbf058a5" diff --git a/rnd/nextgenautogpt/pyproject.toml b/rnd/nextgenautogpt/pyproject.toml new file mode 100644 index 0000000000..1b3d366bef --- /dev/null +++ b/rnd/nextgenautogpt/pyproject.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "rnd" +version = "0.1.0" +description = "" +authors = ["SwiftyOS "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +click = "^8.1.7" +pydantic = "^2.7.1" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api"